--- /dev/null
+Marvell 88PM800 regulator
+
+Required properties:
+- compatible: "marvell,88pm800"
+- reg: I2C slave address
+- regulators: A node that houses a sub-node for each regulator within the
+ device. Each sub-node is identified using the node's name (or the deprecated
+ regulator-compatible property if present), with valid values listed below.
+ The content of each sub-node is defined by the standard binding for
+ regulators; see regulator.txt.
+
+The valid names for regulators are:
+
+ buck1, buck2, buck3, buck4, buck5, ldo1, ldo2, ldo3, ldo4, ldo5, ldo6, ldo7,
+ ldo8, ldo9, ldo10, ldo11, ldo12, ldo13, ldo14, ldo15, ldo16, ldo17, ldo18, ldo19
+
+Example:
+
+ pmic: 88pm800@31 {
+ compatible = "marvell,88pm800";
+ reg = <0x31>;
+
+ regulators {
+ buck1 {
+ regulator-min-microvolt = <600000>;
+ regulator-max-microvolt = <3950000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+ ldo1 {
+ regulator-min-microvolt = <600000>;
+ regulator-max-microvolt = <15000000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+...
+ };
+ };
--- /dev/null
+Maxim MAX8660 voltage regulator
+
+Required properties:
+- compatible: must be one of "maxim,max8660", "maxim,max8661"
+- reg: I2C slave address, usually 0x34
+- any required generic properties defined in regulator.txt
+
+Example:
+
+ i2c_master {
+ max8660@34 {
+ compatible = "maxim,max8660";
+ reg = <0x34>;
+
+ regulators {
+ regulator@0 {
+ regulator-compatible= "V3(DCDC)";
+ regulator-min-microvolt = <725000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ regulator@1 {
+ regulator-compatible= "V4(DCDC)";
+ regulator-min-microvolt = <725000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ regulator@2 {
+ regulator-compatible= "V5(LDO)";
+ regulator-min-microvolt = <1700000>;
+ regulator-max-microvolt = <2000000>;
+ };
+
+ regulator@3 {
+ regulator-compatible= "V6(LDO)";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ regulator@4 {
+ regulator-compatible= "V7(LDO)";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ };
+ };
+ };
+ };
Additional custom properties are listed below.
For ti,palmas-pmic - smps12, smps123, smps3 depending on OTP,
- smps45, smps457, smps7 depending on variant, smps6, smps[8-10],
- ldo[1-9], ldoln, ldousb.
+ smps45, smps457, smps7 depending on variant, smps6, smps[8-9],
+ smps10_out2, smps10_out1, do[1-9], ldoln, ldousb.
Optional sub-node properties:
ti,warm-reset - maintain voltage during warm reset(boolean)
--- /dev/null
+PFUZE100 family of regulators
+
+Required properties:
+- compatible: "fsl,pfuze100"
+- reg: I2C slave address
+
+Required child node:
+- regulators: This is the list of child nodes that specify the regulator
+ initialization data for defined regulators. Please refer to below doc
+ Documentation/devicetree/bindings/regulator/regulator.txt.
+
+ The valid names for regulators are:
+ sw1ab,sw1c,sw2,sw3a,sw3b,sw4,swbst,vsnvs,vrefddr,vgen1~vgen6
+
+Each regulator is defined using the standard binding for regulators.
+
+Example:
+
+ pmic: pfuze100@08 {
+ compatible = "fsl,pfuze100";
+ reg = <0x08>;
+
+ regulators {
+ sw1a_reg: sw1ab {
+ regulator-min-microvolt = <300000>;
+ regulator-max-microvolt = <1875000>;
+ regulator-boot-on;
+ regulator-always-on;
+ regulator-ramp-delay = <6250>;
+ };
+
+ sw1c_reg: sw1c {
+ regulator-min-microvolt = <300000>;
+ regulator-max-microvolt = <1875000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw2_reg: sw2 {
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw3a_reg: sw3a {
+ regulator-min-microvolt = <400000>;
+ regulator-max-microvolt = <1975000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw3b_reg: sw3b {
+ regulator-min-microvolt = <400000>;
+ regulator-max-microvolt = <1975000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ sw4_reg: sw4 {
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ swbst_reg: swbst {
+ regulator-min-microvolt = <5000000>;
+ regulator-max-microvolt = <5150000>;
+ };
+
+ snvs_reg: vsnvs {
+ regulator-min-microvolt = <1000000>;
+ regulator-max-microvolt = <3000000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ vref_reg: vrefddr {
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ vgen1_reg: vgen1 {
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1550000>;
+ };
+
+ vgen2_reg: vgen2 {
+ regulator-min-microvolt = <800000>;
+ regulator-max-microvolt = <1550000>;
+ };
+
+ vgen3_reg: vgen3 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ vgen4_reg: vgen4 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ vgen5_reg: vgen5 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ vgen6_reg: vgen6 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+ };
+ };
- regulator-allow-bypass: allow the regulator to go into bypass mode
- <name>-supply: phandle to the parent supply/regulator node
- regulator-ramp-delay: ramp delay for regulator(in uV/uS)
+ For hardwares which support disabling ramp rate, it should be explicitly
+ intialised to zero (regulator-ramp-delay = <0>) for disabling ramp delay.
Deprecated properties:
- regulator-compatible: If a regulator chip contains multiple
--- /dev/null
+* Energy Micro EFM32 SPI
+
+Required properties:
+- #address-cells: see spi-bus.txt
+- #size-cells: see spi-bus.txt
+- compatible: should be "efm32,spi"
+- reg: Offset and length of the register set for the controller
+- interrupts: pair specifying rx and tx irq
+- clocks: phandle to the spi clock
+- cs-gpios: see spi-bus.txt
+- location: Value to write to the ROUTE register's LOCATION bitfield to configure the pinmux for the device, see datasheet for values.
+
+Example:
+
+spi1: spi@0x4000c400 { /* USART1 */
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "efm32,spi";
+ reg = <0x4000c400 0x400>;
+ interrupts = <15 16>;
+ clocks = <&cmu 20>;
+ cs-gpios = <&gpio 51 1>; // D3
+ location = <1>;
+ status = "ok";
+
+ ks8851@0 {
+ compatible = "ks8851";
+ spi-max-frequency = <6000000>;
+ reg = <0>;
+ interrupt-parent = <&boardfpga>;
+ interrupts = <4>;
+ status = "ok";
+ };
+};
chip select active high
- spi-3wire - (optional) Empty property indicating device requires
3-wire mode.
+- spi-tx-bus-width - (optional) The bus width(number of data wires) that
+ used for MOSI. Defaults to 1 if not present.
+- spi-rx-bus-width - (optional) The bus width(number of data wires) that
+ used for MISO. Defaults to 1 if not present.
+
+Some SPI controllers and devices support Dual and Quad SPI transfer mode.
+It allows data in SPI system transfered in 2 wires(DUAL) or 4 wires(QUAD).
+Now the value that spi-tx-bus-width and spi-rx-bus-width can receive is
+only 1(SINGLE), 2(DUAL) and 4(QUAD).
+Dual/Quad mode is not allowed when 3-wire mode is used.
If a gpio chipselect is used for the SPI slave the gpio number will be passed
via the cs_gpio
--- /dev/null
+ARM Freescale DSPI controller
+
+Required properties:
+- compatible : "fsl,vf610-dspi"
+- reg : Offset and length of the register set for the device
+- interrupts : Should contain SPI controller interrupt
+- clocks: from common clock binding: handle to dspi clock.
+- clock-names: from common clock binding: Shall be "dspi".
+- pinctrl-0: pin control group to be used for this controller.
+- pinctrl-names: must contain a "default" entry.
+- spi-num-chipselects : the number of the chipselect signals.
+- bus-num : the slave chip chipselect signal number.
+Example:
+
+dspi0@4002c000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,vf610-dspi";
+ reg = <0x4002c000 0x1000>;
+ interrupts = <0 67 0x04>;
+ clocks = <&clks VF610_CLK_DSPI0>;
+ clock-names = "dspi";
+ spi-num-chipselects = <5>;
+ bus-num = <0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_dspi0_1>;
+ status = "okay";
+
+ sflash: at26df081a@0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "atmel,at26df081a";
+ spi-max-frequency = <16000000>;
+ spi-cpol;
+ spi-cpha;
+ reg = <0>;
+ linux,modalias = "m25p80";
+ modal = "at26df081a";
+ };
+};
+
+
--- /dev/null
+TI QSPI controller.
+
+Required properties:
+- compatible : should be "ti,dra7xxx-qspi" or "ti,am4372-qspi".
+- reg: Should contain QSPI registers location and length.
+- #address-cells, #size-cells : Must be present if the device has sub-nodes
+- ti,hwmods: Name of the hwmod associated to the QSPI
+
+Recommended properties:
+- spi-max-frequency: Definition as per
+ Documentation/devicetree/bindings/spi/spi-bus.txt
+
+Example:
+
+qspi: qspi@4b300000 {
+ compatible = "ti,dra7xxx-qspi";
+ reg = <0x4b300000 0x100>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ spi-max-frequency = <25000000>;
+ ti,hwmods = "qspi";
+};
Prefix: 'ads1015'
Datasheet: Publicly available at the Texas Instruments website :
http://focus.ti.com/lit/ds/symlink/ads1015.pdf
+ * Texas Instruments ADS1115
+ Prefix: 'ads1115'
+ Datasheet: Publicly available at the Texas Instruments website :
+ http://focus.ti.com/lit/ds/symlink/ads1115.pdf
Authors:
Dirk Eibach, Guntermann & Drunck GmbH <eibach@gdsys.de>
Description
-----------
-This driver implements support for the Texas Instruments ADS1015.
+This driver implements support for the Texas Instruments ADS1015/ADS1115.
-This device is a 12-bit A-D converter with 4 inputs.
+This device is a 12/16-bit A-D converter with 4 inputs.
The inputs can be used single ended or in certain differential combinations.
--- /dev/null
+Kernel driver htu21
+===================
+
+Supported chips:
+ * Measurement Specialties HTU21D
+ Prefix: 'htu21'
+ Addresses scanned: none
+ Datasheet: Publicly available at the Measurement Specialties website
+ http://www.meas-spec.com/downloads/HTU21D.pdf
+
+
+Author:
+ William Markezana <william.markezana@meas-spec.com>
+
+Description
+-----------
+
+The HTU21D is a humidity and temperature sensor in a DFN package of
+only 3 x 3 mm footprint and 0.9 mm height.
+
+The devices communicate with the I2C protocol. All sensors are set to the
+same I2C address 0x40, so an entry with I2C_BOARD_INFO("htu21", 0x40) can
+be used in the board setup code.
+
+This driver does not auto-detect devices. You will have to instantiate the
+devices explicitly. Please see Documentation/i2c/instantiating-devices
+for details.
+
+sysfs-Interface
+---------------
+
+temp1_input - temperature input
+humidity1_input - humidity input
+
+Notes
+-----
+
+The driver uses the default resolution settings of 12 bit for humidity and 14
+bit for temperature, which results in typical measurement times of 11 ms for
+humidity and 44 ms for temperature. To keep self heating below 0.1 degree
+Celsius, the device should not be active for more than 10% of the time. For
+this reason, the driver performs no more than two measurements per second and
+reports cached information if polled more frequently.
+
+Different resolutions, the on-chip heater, using the CRC checksum and reading
+the serial number are not supported yet.
* AMD Family 12h processors: "Llano" (E2/A4/A6/A8-Series)
* AMD Family 14h processors: "Brazos" (C/E/G/Z-Series)
* AMD Family 15h processors: "Bulldozer" (FX-Series), "Trinity"
+* AMD Family 16h processors: "Kabini"
Prefix: 'k10temp'
Addresses scanned: PCI space
improve throughput, but will also increase the
amount of memory reserved for use by the client.
- swapaccount[=0|1]
+ swapaccount=[0|1]
[KNL] Enable accounting of swap in memory resource
controller if no parameter or 1 is given or disable
it if 0 is given (See Documentation/cgroups/memory.txt)
/* if your mach-* infrastructure doesn't support kernels that can
* run on multiple boards, pdata wouldn't benefit from "__init".
*/
- static struct mysoc_spi_data __initdata pdata = { ... };
+ static struct mysoc_spi_data pdata __initdata = { ... };
static __init board_init(void)
{
F: include/linux/i2c-omap.h
OMAP DEVICE TREE SUPPORT
-M: Benoît Cousson <b-cousson@ti.com>
+M: Benoît Cousson <bcousson@baylibre.com>
M: Tony Lindgren <tony@atomide.com>
L: linux-omap@vger.kernel.org
L: devicetree@vger.kernel.org
F: drivers/char/hw_random/omap-rng.c
OMAP HWMOD SUPPORT
-M: Benoît Cousson <b-cousson@ti.com>
+M: Benoît Cousson <bcousson@baylibre.com>
M: Paul Walmsley <paul@pwsan.com>
L: linux-omap@vger.kernel.org
S: Maintained
F: arch/arm/mach-omap2/omap_hwmod.*
OMAP HWMOD DATA FOR OMAP4-BASED DEVICES
-M: Benoît Cousson <b-cousson@ti.com>
+M: Benoît Cousson <bcousson@baylibre.com>
L: linux-omap@vger.kernel.org
S: Maintained
F: arch/arm/mach-omap2/omap_hwmod_44xx_data.c
M: Pawel Moll <pawel.moll@arm.com>
M: Mark Rutland <mark.rutland@arm.com>
M: Stephen Warren <swarren@wwwdotorg.org>
-M: Ian Campbell <ian.campbell@citrix.com>
+M: Ian Campbell <ijc+devicetree@hellion.org.uk>
L: devicetree@vger.kernel.org
S: Maintained
F: Documentation/devicetree/
VERSION = 3
PATCHLEVEL = 11
SUBLEVEL = 0
-EXTRAVERSION = -rc6
+EXTRAVERSION =
NAME = Linux for Workgroups
# *DOCUMENTATION*
ld.a r2,[r0,4]
sub r12,r6,r7
bic r12,r12,r6
+#ifdef __LITTLE_ENDIAN__
and r7,r12,r4
breq r7,0,.Loop ; For speed, we want this branch to be unaligned.
b .Lfound_char ; Likewise this one.
+#else
+ and r12,r12,r4
+ breq r12,0,.Loop ; For speed, we want this branch to be unaligned.
+ lsr_s r12,r12,7
+ bic r2,r7,r6
+ b.d .Lfound_char_b
+ and_s r2,r2,r12
+#endif
; /* We require this code address to be unaligned for speed... */
.Laligned:
ld_s r2,[r0]
lsr r7,r7,7
bic r2,r7,r6
+.Lfound_char_b:
norm r2,r2
sub_s r0,r0,4
asr_s r2,r2,3
compatible = "atmel,at91sam9n12ek", "atmel,at91sam9n12", "atmel,at91sam9";
chosen {
- bootargs = "mem=128M console=ttyS0,115200 root=/dev/mtdblock1 rw rootfstype=jffs2";
+ bootargs = "console=ttyS0,115200 root=/dev/mtdblock1 rw rootfstype=jffs2";
};
memory {
- reg = <0x20000000 0x10000000>;
+ reg = <0x20000000 0x8000000>;
};
clocks {
usb0: ohci@00600000 {
status = "okay";
- num-ports = <2>;
- atmel,vbus-gpio = <&pioD 19 GPIO_ACTIVE_LOW
+ num-ports = <3>;
+ atmel,vbus-gpio = <0 /* &pioD 18 GPIO_ACTIVE_LOW *//* Activate to have access to port A */
+ &pioD 19 GPIO_ACTIVE_LOW
&pioD 20 GPIO_ACTIVE_LOW
>;
};
regulator-max-microvolt = <5000000>;
enable-active-high;
gpio = <&gpio 24 0>; /* PD0 */
+ regulator-always-on;
+ regulator-boot-on;
};
};
regulator-max-microvolt = <5000000>;
enable-active-high;
gpio = <&gpio 170 0>; /* PV2 */
+ regulator-always-on;
+ regulator-boot-on;
};
};
regulator-max-microvolt = <5000000>;
enable-active-high;
gpio = <&tca6416 0 0>; /* GPIO_PMU0 */
+ regulator-always-on;
+ regulator-boot-on;
};
vbus3_reg: regulator@3 {
regulator-max-microvolt = <5000000>;
enable-active-high;
gpio = <&tca6416 1 0>; /* GPIO_PMU1 */
+ regulator-always-on;
+ regulator-boot-on;
};
};
memcpy(base + offset, start, length);
if (!cache_is_vipt_nonaliasing())
- flush_icache_range(base + offset, offset + length);
+ flush_icache_range((unsigned long)base + offset, offset +
+ length);
flush_icache_range(0xffff0000 + offset, 0xffff0000 + offset + length);
}
crash_save_cpu(®s, smp_processor_id());
flush_cache_all();
+ set_cpu_online(smp_processor_id(), false);
atomic_dec(&waiting_for_crash_ipi);
while (1)
cpu_relax();
#define access_pmintenclr pm_fake
/* Architected CP15 registers.
- * Important: Must be sorted ascending by CRn, CRM, Op1, Op2
+ * CRn denotes the primary register number, but is copied to the CRm in the
+ * user space API for 64-bit register access in line with the terminology used
+ * in the ARM ARM.
+ * Important: Must be sorted ascending by CRn, CRM, Op1, Op2 and with 64-bit
+ * registers preceding 32-bit ones.
*/
static const struct coproc_reg cp15_regs[] = {
/* CSSELR: swapped by interrupt.S. */
NULL, reset_unknown, c0_CSSELR },
/* TTBR0/TTBR1: swapped by interrupt.S. */
- { CRm( 2), Op1( 0), is64, NULL, reset_unknown64, c2_TTBR0 },
- { CRm( 2), Op1( 1), is64, NULL, reset_unknown64, c2_TTBR1 },
+ { CRm64( 2), Op1( 0), is64, NULL, reset_unknown64, c2_TTBR0 },
+ { CRm64( 2), Op1( 1), is64, NULL, reset_unknown64, c2_TTBR1 },
/* TTBCR: swapped by interrupt.S. */
{ CRn( 2), CRm( 0), Op1( 0), Op2( 2), is32,
NULL, reset_unknown, c6_IFAR },
/* PAR swapped by interrupt.S */
- { CRn( 7), Op1( 0), is64, NULL, reset_unknown64, c7_PAR },
+ { CRm64( 7), Op1( 0), is64, NULL, reset_unknown64, c7_PAR },
/*
* DC{C,I,CI}SW operations:
| KVM_REG_ARM_OPC1_MASK))
return false;
params->is_64bit = true;
- params->CRm = ((id & KVM_REG_ARM_CRM_MASK)
+ /* CRm to CRn: see cp15_to_index for details */
+ params->CRn = ((id & KVM_REG_ARM_CRM_MASK)
>> KVM_REG_ARM_CRM_SHIFT);
params->Op1 = ((id & KVM_REG_ARM_OPC1_MASK)
>> KVM_REG_ARM_OPC1_SHIFT);
params->Op2 = 0;
- params->CRn = 0;
+ params->CRm = 0;
return true;
default:
return false;
if (reg->is_64) {
val |= KVM_REG_SIZE_U64;
val |= (reg->Op1 << KVM_REG_ARM_OPC1_SHIFT);
- val |= (reg->CRm << KVM_REG_ARM_CRM_SHIFT);
+ /*
+ * CRn always denotes the primary coproc. reg. nr. for the
+ * in-kernel representation, but the user space API uses the
+ * CRm for the encoding, because it is modelled after the
+ * MRRC/MCRR instructions: see the ARM ARM rev. c page
+ * B3-1445
+ */
+ val |= (reg->CRn << KVM_REG_ARM_CRM_SHIFT);
} else {
val |= KVM_REG_SIZE_U32;
val |= (reg->Op1 << KVM_REG_ARM_OPC1_SHIFT);
return -1;
if (i1->CRn != i2->CRn)
return i1->CRn - i2->CRn;
+ if (i1->is_64 != i2->is_64)
+ return i2->is_64 - i1->is_64;
if (i1->CRm != i2->CRm)
return i1->CRm - i2->CRm;
if (i1->Op1 != i2->Op1)
#define CRn(_x) .CRn = _x
#define CRm(_x) .CRm = _x
+#define CRm64(_x) .CRn = _x, .CRm = 0
#define Op1(_x) .Op1 = _x
#define Op2(_x) .Op2 = _x
#define is64 .is_64 = true
/*
* A15-specific CP15 registers.
- * Important: Must be sorted ascending by CRn, CRM, Op1, Op2
+ * CRn denotes the primary register number, but is copied to the CRm in the
+ * user space API for 64-bit register access in line with the terminology used
+ * in the ARM ARM.
+ * Important: Must be sorted ascending by CRn, CRM, Op1, Op2 and with 64-bit
+ * registers preceding 32-bit ones.
*/
static const struct coproc_reg a15_regs[] = {
/* MPIDR: we use VMPIDR for guest access. */
static int decode_hsr(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
struct kvm_exit_mmio *mmio)
{
- unsigned long rt, len;
+ unsigned long rt;
+ int len;
bool is_write, sign_extend;
if (kvm_vcpu_dabt_isextabt(vcpu)) {
return p;
}
+static bool page_empty(void *ptr)
+{
+ struct page *ptr_page = virt_to_page(ptr);
+ return page_count(ptr_page) == 1;
+}
+
static void clear_pud_entry(struct kvm *kvm, pud_t *pud, phys_addr_t addr)
{
pmd_t *pmd_table = pmd_offset(pud, 0);
put_page(virt_to_page(pmd));
}
-static bool pmd_empty(pmd_t *pmd)
-{
- struct page *pmd_page = virt_to_page(pmd);
- return page_count(pmd_page) == 1;
-}
-
static void clear_pte_entry(struct kvm *kvm, pte_t *pte, phys_addr_t addr)
{
if (pte_present(*pte)) {
}
}
-static bool pte_empty(pte_t *pte)
-{
- struct page *pte_page = virt_to_page(pte);
- return page_count(pte_page) == 1;
-}
-
static void unmap_range(struct kvm *kvm, pgd_t *pgdp,
unsigned long long start, u64 size)
{
pmd_t *pmd;
pte_t *pte;
unsigned long long addr = start, end = start + size;
- u64 range;
+ u64 next;
while (addr < end) {
pgd = pgdp + pgd_index(addr);
pud = pud_offset(pgd, addr);
if (pud_none(*pud)) {
- addr += PUD_SIZE;
+ addr = pud_addr_end(addr, end);
continue;
}
pmd = pmd_offset(pud, addr);
if (pmd_none(*pmd)) {
- addr += PMD_SIZE;
+ addr = pmd_addr_end(addr, end);
continue;
}
pte = pte_offset_kernel(pmd, addr);
clear_pte_entry(kvm, pte, addr);
- range = PAGE_SIZE;
+ next = addr + PAGE_SIZE;
/* If we emptied the pte, walk back up the ladder */
- if (pte_empty(pte)) {
+ if (page_empty(pte)) {
clear_pmd_entry(kvm, pmd, addr);
- range = PMD_SIZE;
- if (pmd_empty(pmd)) {
+ next = pmd_addr_end(addr, end);
+ if (page_empty(pmd) && !page_empty(pud)) {
clear_pud_entry(kvm, pud, addr);
- range = PUD_SIZE;
+ next = pud_addr_end(addr, end);
}
}
- addr += range;
+ addr = next;
}
}
CLKDEV_CON_DEV_ID("usart", "f8020000.serial", &usart1_clk),
CLKDEV_CON_DEV_ID("usart", "f8024000.serial", &usart2_clk),
CLKDEV_CON_DEV_ID("usart", "f8028000.serial", &usart3_clk),
+ CLKDEV_CON_DEV_ID("usart", "f8040000.serial", &uart0_clk),
+ CLKDEV_CON_DEV_ID("usart", "f8044000.serial", &uart1_clk),
CLKDEV_CON_DEV_ID("t0_clk", "f8008000.timer", &tcb0_clk),
CLKDEV_CON_DEV_ID("t0_clk", "f800c000.timer", &tcb0_clk),
CLKDEV_CON_DEV_ID("mci_clk", "f0008000.mmc", &mmc0_clk),
.parts = davinci_nand_partitions,
.nr_parts = ARRAY_SIZE(davinci_nand_partitions),
.ecc_mode = NAND_ECC_HW_SYNDROME,
+ .ecc_bits = 4,
.bbt_options = NAND_BBT_USE_FLASH,
};
.parts = davinci_evm_nandflash_partition,
.nr_parts = ARRAY_SIZE(davinci_evm_nandflash_partition),
.ecc_mode = NAND_ECC_HW,
+ .ecc_bits = 1,
.bbt_options = NAND_BBT_USE_FLASH,
.timing = &davinci_evm_nandflash_timing,
};
.parts = davinci_nand_partitions,
.nr_parts = ARRAY_SIZE(davinci_nand_partitions),
.ecc_mode = NAND_ECC_HW,
+ .ecc_bits = 1,
.options = 0,
};
.parts = davinci_ntosd2_nandflash_partition,
.nr_parts = ARRAY_SIZE(davinci_ntosd2_nandflash_partition),
.ecc_mode = NAND_ECC_HW,
+ .ecc_bits = 1,
.bbt_options = NAND_BBT_USE_FLASH,
};
};
static struct musb_hdrc_platform_data tusb_data = {
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
.mode = MUSB_OTG,
-#else
- .mode = MUSB_HOST,
-#endif
.set_power = tusb_set_power,
.min_power = 25, /* x2 = 50 mA drawn from VBUS as peripheral */
.power = 100, /* Max 100 mA VBUS for host mode */
static struct omap_musb_board_data musb_board_data = {
.interface_type = MUSB_INTERFACE_ULPI,
- .mode = MUSB_PERIPHERAL,
+ .mode = MUSB_OTG,
.power = 0,
};
};
static struct musb_hdrc_platform_data musb_plat = {
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
.mode = MUSB_OTG,
-#else
- .mode = MUSB_HOST,
-#endif
+
/* .clock is set dynamically */
.config = &musb_config,
DT_MACHINE_START(ATLAS6_DT, "Generic ATLAS6 (Flattened Device Tree)")
/* Maintainer: Barry Song <baohua.song@csr.com> */
- .nr_irqs = 128,
.map_io = sirfsoc_map_io,
.init_time = sirfsoc_init_time,
.init_late = sirfsoc_init_late,
DT_MACHINE_START(PRIMA2_DT, "Generic PRIMA2 (Flattened Device Tree)")
/* Maintainer: Barry Song <baohua.song@csr.com> */
- .nr_irqs = 128,
.map_io = sirfsoc_map_io,
.init_time = sirfsoc_init_time,
.dma_zone_size = SZ_256M,
the CPU type fitted to the system. This permits binaries to be
run on ARMv4 through to ARMv7 without modification.
+ See Documentation/arm/kernel_user_helpers.txt for details.
+
However, the fixed address nature of these helpers can be used
by ROP (return orientated programming) authors when creating
exploits.
If all of the binaries and libraries which run on your platform
are built specifically for your platform, and make no use of
- these helpers, then you can turn this option off. However,
- when such an binary or library is run, it will receive a SIGILL
- signal, which will terminate the program.
+ these helpers, then you can turn this option off to hinder
+ such exploits. However, in that case, if a binary or library
+ relying on those helpers is run, it will receive a SIGILL signal,
+ which will terminate the program.
Say N here only if you are absolutely certain that you do not
need these helpers; otherwise, the safe option is to say Y.
printk("CPU %s (id 0x%08lx)\n", cpu->name, idcode);
- if (cpu->map_io == NULL || cpu->init == NULL) {
+ if (cpu->init == NULL) {
printk(KERN_ERR "CPU %s support not enabled\n", cpu->name);
panic("Unsupported Samsung CPU");
}
- cpu->map_io();
+ if (cpu->map_io)
+ cpu->map_io();
}
/* s3c24xx_init_clocks
per_cpu(xen_vcpu, cpu) = vcpup;
enable_percpu_irq(xen_events_irq, 0);
+ put_cpu();
}
static void xen_restart(enum reboot_mode reboot_mode, const char *cmd)
#define TPIDR_EL1 18 /* Thread ID, Privileged */
#define AMAIR_EL1 19 /* Aux Memory Attribute Indirection Register */
#define CNTKCTL_EL1 20 /* Timer Control Register (EL1) */
+#define PAR_EL1 21 /* Physical Address Register */
/* 32bit specific registers. Keep them at the end of the range */
-#define DACR32_EL2 21 /* Domain Access Control Register */
-#define IFSR32_EL2 22 /* Instruction Fault Status Register */
-#define FPEXC32_EL2 23 /* Floating-Point Exception Control Register */
-#define DBGVCR32_EL2 24 /* Debug Vector Catch Register */
-#define TEECR32_EL1 25 /* ThumbEE Configuration Register */
-#define TEEHBR32_EL1 26 /* ThumbEE Handler Base Register */
-#define NR_SYS_REGS 27
+#define DACR32_EL2 22 /* Domain Access Control Register */
+#define IFSR32_EL2 23 /* Instruction Fault Status Register */
+#define FPEXC32_EL2 24 /* Floating-Point Exception Control Register */
+#define DBGVCR32_EL2 25 /* Debug Vector Catch Register */
+#define TEECR32_EL1 26 /* ThumbEE Configuration Register */
+#define TEEHBR32_EL1 27 /* ThumbEE Handler Base Register */
+#define NR_SYS_REGS 28
/* 32bit mapping */
#define c0_MPIDR (MPIDR_EL1 * 2) /* MultiProcessor ID Register */
#define c5_AIFSR (AFSR1_EL1 * 2) /* Auxiliary Instr Fault Status R */
#define c6_DFAR (FAR_EL1 * 2) /* Data Fault Address Register */
#define c6_IFAR (c6_DFAR + 1) /* Instruction Fault Address Register */
+#define c7_PAR (PAR_EL1 * 2) /* Physical Address Register */
+#define c7_PAR_high (c7_PAR + 1) /* PAR top 32 bits */
#define c10_PRRR (MAIR_EL1 * 2) /* Primary Region Remap Register */
#define c10_NMRR (c10_PRRR + 1) /* Normal Memory Remap Register */
#define c12_VBAR (VBAR_EL1 * 2) /* Vector Base Address Register */
struct kvm_mmu_memory_cache mmu_page_cache;
/* Target CPU and feature flags */
- u32 target;
+ int target;
DECLARE_BITMAP(features, KVM_VCPU_MAX_FEATURES);
/* Detect first run of a vcpu */
static int
armpmu_map_event(const unsigned (*event_map)[PERF_COUNT_HW_MAX], u64 config)
{
- int mapping = (*event_map)[config];
+ int mapping;
+
+ if (config >= PERF_COUNT_HW_MAX)
+ return -EINVAL;
+
+ mapping = (*event_map)[config];
return mapping == HW_OP_UNSUPPORTED ? -ENOENT : mapping;
}
struct hw_perf_event fake_event = event->hw;
struct pmu *leader_pmu = event->group_leader->pmu;
+ if (is_software_event(event))
+ return 1;
+
if (event->pmu != leader_pmu || event->state <= PERF_EVENT_STATE_OFF)
return 1;
mrs x21, tpidr_el1
mrs x22, amair_el1
mrs x23, cntkctl_el1
+ mrs x24, par_el1
stp x4, x5, [x3]
stp x6, x7, [x3, #16]
stp x18, x19, [x3, #112]
stp x20, x21, [x3, #128]
stp x22, x23, [x3, #144]
+ str x24, [x3, #160]
.endm
.macro restore_sysregs
ldp x18, x19, [x3, #112]
ldp x20, x21, [x3, #128]
ldp x22, x23, [x3, #144]
+ ldr x24, [x3, #160]
msr vmpidr_el2, x4
msr csselr_el1, x5
msr tpidr_el1, x21
msr amair_el1, x22
msr cntkctl_el1, x23
+ msr par_el1, x24
.endm
.macro skip_32bit_state tmp, target
// void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa);
ENTRY(__kvm_tlb_flush_vmid_ipa)
+ dsb ishst
+
kern_hyp_va x0
ldr x2, [x0, #KVM_VTTBR]
msr vttbr_el2, x2
ENDPROC(__kvm_tlb_flush_vmid_ipa)
ENTRY(__kvm_flush_vm_context)
+ dsb ishst
tlbi alle1is
ic ialluis
dsb sy
*/
tbnz x1, #7, 1f // S1PTW is set
+ /* Preserve PAR_EL1 */
+ mrs x3, par_el1
+ push x3, xzr
+
/*
* Permission fault, HPFAR_EL2 is invalid.
* Resolve the IPA the hard way using the guest VA.
/* Read result */
mrs x3, par_el1
+ pop x0, xzr // Restore PAR_EL1 from the stack
+ msr par_el1, x0
tbnz x3, #0, 3f // Bail out if we failed the translation
ubfx x3, x3, #12, #36 // Extract IPA
lsl x3, x3, #4 // and present it like HPFAR
/* FAR_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b0110), CRm(0b0000), Op2(0b000),
NULL, reset_unknown, FAR_EL1 },
+ /* PAR_EL1 */
+ { Op0(0b11), Op1(0b000), CRn(0b0111), CRm(0b0100), Op2(0b000),
+ NULL, reset_unknown, PAR_EL1 },
/* PMINTENSET_EL1 */
{ Op0(0b11), Op1(0b000), CRn(0b1001), CRm(0b1110), Op2(0b001),
#include <asm/machdep.h>
#include <asm/natfeat.h>
-extern long nf_get_id2(const char *feature_name);
+extern long nf_get_id_phys(unsigned long feature_name);
asm("\n"
-" .global nf_get_id2,nf_call\n"
-"nf_get_id2:\n"
+" .global nf_get_id_phys,nf_call\n"
+"nf_get_id_phys:\n"
" .short 0x7300\n"
" rts\n"
"nf_call:\n"
"1: moveq.l #0,%d0\n"
" rts\n"
" .section __ex_table,\"a\"\n"
-" .long nf_get_id2,1b\n"
+" .long nf_get_id_phys,1b\n"
" .long nf_call,1b\n"
" .previous");
EXPORT_SYMBOL_GPL(nf_call);
if (n >= sizeof(name_copy))
return 0;
- return nf_get_id2(name_copy);
+ return nf_get_id_phys(virt_to_phys(name_copy));
}
EXPORT_SYMBOL_GPL(nf_get_id);
va_start(ap, fmt);
n = vsnprintf(buf, 256, fmt, ap);
- nf_call(nf_get_id("NF_STDERR"), buf);
+ nf_call(nf_get_id("NF_STDERR"), virt_to_phys(buf));
va_end(ap);
}
id = nf_get_id("NF_NAME");
if (!id)
return;
- nf_call(id, buf, 256);
+ nf_call(id, virt_to_phys(buf), 256);
buf[255] = 0;
pr_info("NatFeats found (%s, %lu.%lu)\n", buf, version >> 16,
static inline s32 nfhd_get_capacity(u32 major, u32 minor, u32 *blocks,
u32 *blocksize)
{
- return nf_call(nfhd_id + NFHD_GET_CAPACITY, major, minor, blocks,
- blocksize);
+ return nf_call(nfhd_id + NFHD_GET_CAPACITY, major, minor,
+ virt_to_phys(blocks), virt_to_phys(blocksize));
}
static LIST_HEAD(nfhd_list);
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/uaccess.h>
+#include <linux/io.h>
#include <asm/natfeat.h>
static void nfputs(const char *str, unsigned int count)
{
char buf[68];
+ unsigned long phys = virt_to_phys(buf);
buf[64] = 0;
while (count > 64) {
memcpy(buf, str, 64);
- nf_call(stderr_id, buf);
+ nf_call(stderr_id, phys);
str += 64;
count -= 64;
}
memcpy(buf, str, count);
buf[count] = 0;
- nf_call(stderr_id, buf);
+ nf_call(stderr_id, phys);
}
static void nfcon_write(struct console *con, const char *str,
{
char temp[2] = { ch, 0 };
- nf_call(stderr_id, temp);
+ nf_call(stderr_id, virt_to_phys(temp));
return 1;
}
char mac[ETH_ALEN], host_ip[32], local_ip[32];
int err;
- if (!nf_call(nfEtherID + XIF_GET_MAC, unit, mac, ETH_ALEN))
+ if (!nf_call(nfEtherID + XIF_GET_MAC, unit, virt_to_phys(mac),
+ ETH_ALEN))
return NULL;
dev = alloc_etherdev(sizeof(struct nfeth_private));
}
nf_call(nfEtherID + XIF_GET_IPHOST, unit,
- host_ip, sizeof(host_ip));
+ virt_to_phys(host_ip), sizeof(host_ip));
nf_call(nfEtherID + XIF_GET_IPATARI, unit,
- local_ip, sizeof(local_ip));
+ virt_to_phys(local_ip), sizeof(local_ip));
netdev_info(dev, KBUILD_MODNAME " addr:%s (%s) HWaddr:%pM\n", host_ip,
local_ip, mac);
static inline bool arch_irqs_disabled_flags(unsigned long flags)
{
+ if (MACH_IS_ATARI) {
+ /* Ignore HSYNC = ipl 2 on Atari */
+ return (flags & ~(ALLOWINT | 0x200)) != 0;
+ }
return (flags & ~ALLOWINT) != 0;
}
dec_insn.next_pc_inc;
return 1;
break;
+#ifdef CONFIG_CPU_CAVIUM_OCTEON
+ case lwc2_op: /* This is bbit0 on Octeon */
+ if ((regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt)) == 0)
+ *contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2);
+ else
+ *contpc = regs->cp0_epc + 8;
+ return 1;
+ case ldc2_op: /* This is bbit032 on Octeon */
+ if ((regs->regs[insn.i_format.rs] & (1ull<<(insn.i_format.rt + 32))) == 0)
+ *contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2);
+ else
+ *contpc = regs->cp0_epc + 8;
+ return 1;
+ case swc2_op: /* This is bbit1 on Octeon */
+ if (regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt))
+ *contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2);
+ else
+ *contpc = regs->cp0_epc + 8;
+ return 1;
+ case sdc2_op: /* This is bbit132 on Octeon */
+ if (regs->regs[insn.i_format.rs] & (1ull<<(insn.i_format.rt + 32)))
+ *contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2);
+ else
+ *contpc = regs->cp0_epc + 8;
+ return 1;
+#endif
case cop0_op:
case cop1_op:
case cop2_op:
must live at a different physical address than the primary
kernel.
+# This value must have zeroes in the bottom 60 bits otherwise lots will break
config PAGE_OFFSET
hex
default "0xc000000000000000"
#define __va(x) ((void *)(unsigned long)((phys_addr_t)(x) + VIRT_PHYS_OFFSET))
#define __pa(x) ((unsigned long)(x) - VIRT_PHYS_OFFSET)
#else
+#ifdef CONFIG_PPC64
+/*
+ * gcc miscompiles (unsigned long)(&static_var) - PAGE_OFFSET
+ * with -mcmodel=medium, so we use & and | instead of - and + on 64-bit.
+ */
+#define __va(x) ((void *)(unsigned long)((phys_addr_t)(x) | PAGE_OFFSET))
+#define __pa(x) ((unsigned long)(x) & 0x0fffffffffffffffUL)
+
+#else /* 32-bit, non book E */
#define __va(x) ((void *)(unsigned long)((phys_addr_t)(x) + PAGE_OFFSET - MEMORY_START))
#define __pa(x) ((unsigned long)(x) - PAGE_OFFSET + MEMORY_START)
#endif
+#endif
/*
* Unfortunately the PLT is in the BSS in the PPC32 ELF ABI,
#include <asm/vdso_datapage.h>
#include <asm/vio.h>
#include <asm/mmu.h>
+#include <asm/machdep.h>
+
+/*
+ * This isn't a module but we expose that to userspace
+ * via /proc so leave the definitions here
+ */
#define MODULE_VERS "1.9"
#define MODULE_NAME "lparcfg"
{
unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
- if (plpar_hcall(H_GET_EM_PARMS, retbuf) == H_SUCCESS)
+ if (firmware_has_feature(FW_FEATURE_LPAR) &&
+ plpar_hcall(H_GET_EM_PARMS, retbuf) == H_SUCCESS)
seq_printf(m, "power_mode_data=%016lx\n", retbuf[0]);
}
}
static const struct file_operations lparcfg_fops = {
- .owner = THIS_MODULE,
.read = seq_read,
.write = lparcfg_write,
.open = lparcfg_open,
}
return 0;
}
-
-static void __exit lparcfg_cleanup(void)
-{
- remove_proc_subtree("powerpc/lparcfg", NULL);
-}
-
-module_init(lparcfg_init);
-module_exit(lparcfg_cleanup);
-MODULE_DESCRIPTION("Interface for LPAR configuration data");
-MODULE_AUTHOR("Dave Engebretsen");
-MODULE_LICENSE("GPL");
+machine_device_initcall(pseries, lparcfg_init);
select HAVE_FUNCTION_GRAPH_TRACER
select HAVE_FUNCTION_TRACER
select HAVE_FUNCTION_TRACE_MCOUNT_TEST
+ select HAVE_GENERIC_HARDIRQS
select HAVE_KERNEL_BZIP2
select HAVE_KERNEL_GZIP
select HAVE_KERNEL_LZ4
This allows you to specify the maximum number of PCI functions which
this kernel will support.
+config PCI_NR_MSI
+ int "Maximum number of MSI interrupts (64-32768)"
+ range 64 32768
+ default "256"
+ help
+ This defines the number of virtual interrupts the kernel will
+ provide for MSI interrupts. If you configure your system to have
+ too few drivers will fail to allocate MSI interrupts for all
+ PCI devices.
+
source "drivers/pci/Kconfig"
source "drivers/pci/pcie/Kconfig"
source "drivers/pci/hotplug/Kconfig"
#ifndef _ASM_S390_AIRQ_H
#define _ASM_S390_AIRQ_H
+#include <linux/bit_spinlock.h>
+
struct airq_struct {
struct hlist_node list; /* Handler queueing. */
void (*handler)(struct airq_struct *); /* Thin-interrupt handler */
int register_adapter_interrupt(struct airq_struct *airq);
void unregister_adapter_interrupt(struct airq_struct *airq);
+/* Adapter interrupt bit vector */
+struct airq_iv {
+ unsigned long *vector; /* Adapter interrupt bit vector */
+ unsigned long *avail; /* Allocation bit mask for the bit vector */
+ unsigned long *bitlock; /* Lock bit mask for the bit vector */
+ unsigned long *ptr; /* Pointer associated with each bit */
+ unsigned int *data; /* 32 bit value associated with each bit */
+ unsigned long bits; /* Number of bits in the vector */
+ unsigned long end; /* Number of highest allocated bit + 1 */
+ spinlock_t lock; /* Lock to protect alloc & free */
+};
+
+#define AIRQ_IV_ALLOC 1 /* Use an allocation bit mask */
+#define AIRQ_IV_BITLOCK 2 /* Allocate the lock bit mask */
+#define AIRQ_IV_PTR 4 /* Allocate the ptr array */
+#define AIRQ_IV_DATA 8 /* Allocate the data array */
+
+struct airq_iv *airq_iv_create(unsigned long bits, unsigned long flags);
+void airq_iv_release(struct airq_iv *iv);
+unsigned long airq_iv_alloc_bit(struct airq_iv *iv);
+void airq_iv_free_bit(struct airq_iv *iv, unsigned long bit);
+unsigned long airq_iv_scan(struct airq_iv *iv, unsigned long start,
+ unsigned long end);
+
+static inline unsigned long airq_iv_end(struct airq_iv *iv)
+{
+ return iv->end;
+}
+
+static inline void airq_iv_lock(struct airq_iv *iv, unsigned long bit)
+{
+ const unsigned long be_to_le = BITS_PER_LONG - 1;
+ bit_spin_lock(bit ^ be_to_le, iv->bitlock);
+}
+
+static inline void airq_iv_unlock(struct airq_iv *iv, unsigned long bit)
+{
+ const unsigned long be_to_le = BITS_PER_LONG - 1;
+ bit_spin_unlock(bit ^ be_to_le, iv->bitlock);
+}
+
+static inline void airq_iv_set_data(struct airq_iv *iv, unsigned long bit,
+ unsigned int data)
+{
+ iv->data[bit] = data;
+}
+
+static inline unsigned int airq_iv_get_data(struct airq_iv *iv,
+ unsigned long bit)
+{
+ return iv->data[bit];
+}
+
+static inline void airq_iv_set_ptr(struct airq_iv *iv, unsigned long bit,
+ unsigned long ptr)
+{
+ iv->ptr[bit] = ptr;
+}
+
+static inline unsigned long airq_iv_get_ptr(struct airq_iv *iv,
+ unsigned long bit)
+{
+ return iv->ptr[bit];
+}
+
#endif /* _ASM_S390_AIRQ_H */
addr = (unsigned long) ptr + ((nr ^ (BITS_PER_LONG - 8)) >> 3);
asm volatile(
" oc %O0(1,%R0),%1"
- : "=Q" (*(char *) addr) : "Q" (_oi_bitmap[nr & 7]) : "cc" );
+ : "+Q" (*(char *) addr) : "Q" (_oi_bitmap[nr & 7]) : "cc");
}
static inline void
addr = (unsigned long) ptr + ((nr ^ (BITS_PER_LONG - 8)) >> 3);
asm volatile(
" nc %O0(1,%R0),%1"
- : "=Q" (*(char *) addr) : "Q" (_ni_bitmap[nr & 7]) : "cc" );
+ : "+Q" (*(char *) addr) : "Q" (_ni_bitmap[nr & 7]) : "cc");
}
static inline void
addr = (unsigned long) ptr + ((nr ^ (BITS_PER_LONG - 8)) >> 3);
asm volatile(
" xc %O0(1,%R0),%1"
- : "=Q" (*(char *) addr) : "Q" (_oi_bitmap[nr & 7]) : "cc" );
+ : "+Q" (*(char *) addr) : "Q" (_oi_bitmap[nr & 7]) : "cc");
}
static inline void
ch = *(unsigned char *) addr;
asm volatile(
" oc %O0(1,%R0),%1"
- : "=Q" (*(char *) addr) : "Q" (_oi_bitmap[nr & 7])
+ : "+Q" (*(char *) addr) : "Q" (_oi_bitmap[nr & 7])
: "cc", "memory");
return (ch >> (nr & 7)) & 1;
}
ch = *(unsigned char *) addr;
asm volatile(
" nc %O0(1,%R0),%1"
- : "=Q" (*(char *) addr) : "Q" (_ni_bitmap[nr & 7])
+ : "+Q" (*(char *) addr) : "Q" (_ni_bitmap[nr & 7])
: "cc", "memory");
return (ch >> (nr & 7)) & 1;
}
ch = *(unsigned char *) addr;
asm volatile(
" xc %O0(1,%R0),%1"
- : "=Q" (*(char *) addr) : "Q" (_oi_bitmap[nr & 7])
+ : "+Q" (*(char *) addr) : "Q" (_oi_bitmap[nr & 7])
: "cc", "memory");
return (ch >> (nr & 7)) & 1;
}
return 0;
}
+void channel_subsystem_reinit(void);
extern void css_schedule_reprobe(void);
extern void reipl_ccw_dev(struct ccw_dev_id *id);
#define HARDIRQ_BITS 8
+static inline void ack_bad_irq(unsigned int irq)
+{
+ printk(KERN_CRIT "unexpected IRQ trap at vector %02x\n", irq);
+}
+
#endif /* __ASM_HARDIRQ_H */
void set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
pte_t *ptep, pte_t pte);
+pte_t huge_ptep_get(pte_t *ptep);
+pte_t huge_ptep_get_and_clear(struct mm_struct *mm,
+ unsigned long addr, pte_t *ptep);
/*
* If the arch doesn't supply something else, assume that hugepage
int arch_prepare_hugepage(struct page *page);
void arch_release_hugepage(struct page *page);
-static inline pte_t huge_pte_wrprotect(pte_t pte)
+static inline void huge_pte_clear(struct mm_struct *mm, unsigned long addr,
+ pte_t *ptep)
{
- pte_val(pte) |= _PAGE_RO;
- return pte;
+ pte_val(*ptep) = _SEGMENT_ENTRY_EMPTY;
}
-static inline int huge_pte_none(pte_t pte)
+static inline void huge_ptep_clear_flush(struct vm_area_struct *vma,
+ unsigned long address, pte_t *ptep)
{
- return (pte_val(pte) & _SEGMENT_ENTRY_INV) &&
- !(pte_val(pte) & _SEGMENT_ENTRY_RO);
+ huge_ptep_get_and_clear(vma->vm_mm, address, ptep);
}
-static inline pte_t huge_ptep_get(pte_t *ptep)
+static inline int huge_ptep_set_access_flags(struct vm_area_struct *vma,
+ unsigned long addr, pte_t *ptep,
+ pte_t pte, int dirty)
{
- pte_t pte = *ptep;
- unsigned long mask;
-
- if (!MACHINE_HAS_HPAGE) {
- ptep = (pte_t *) (pte_val(pte) & _SEGMENT_ENTRY_ORIGIN);
- if (ptep) {
- mask = pte_val(pte) &
- (_SEGMENT_ENTRY_INV | _SEGMENT_ENTRY_RO);
- pte = pte_mkhuge(*ptep);
- pte_val(pte) |= mask;
- }
+ int changed = !pte_same(huge_ptep_get(ptep), pte);
+ if (changed) {
+ huge_ptep_get_and_clear(vma->vm_mm, addr, ptep);
+ set_huge_pte_at(vma->vm_mm, addr, ptep, pte);
}
- return pte;
+ return changed;
}
-static inline void __pmd_csp(pmd_t *pmdp)
+static inline void huge_ptep_set_wrprotect(struct mm_struct *mm,
+ unsigned long addr, pte_t *ptep)
{
- register unsigned long reg2 asm("2") = pmd_val(*pmdp);
- register unsigned long reg3 asm("3") = pmd_val(*pmdp) |
- _SEGMENT_ENTRY_INV;
- register unsigned long reg4 asm("4") = ((unsigned long) pmdp) + 5;
-
- asm volatile(
- " csp %1,%3"
- : "=m" (*pmdp)
- : "d" (reg2), "d" (reg3), "d" (reg4), "m" (*pmdp) : "cc");
+ pte_t pte = huge_ptep_get_and_clear(mm, addr, ptep);
+ set_huge_pte_at(mm, addr, ptep, pte_wrprotect(pte));
}
-static inline void huge_ptep_invalidate(struct mm_struct *mm,
- unsigned long address, pte_t *ptep)
-{
- pmd_t *pmdp = (pmd_t *) ptep;
-
- if (MACHINE_HAS_IDTE)
- __pmd_idte(address, pmdp);
- else
- __pmd_csp(pmdp);
- pmd_val(*pmdp) = _SEGMENT_ENTRY_INV | _SEGMENT_ENTRY;
-}
-
-static inline pte_t huge_ptep_get_and_clear(struct mm_struct *mm,
- unsigned long addr, pte_t *ptep)
-{
- pte_t pte = huge_ptep_get(ptep);
-
- huge_ptep_invalidate(mm, addr, ptep);
- return pte;
-}
-
-#define huge_ptep_set_access_flags(__vma, __addr, __ptep, __entry, __dirty) \
-({ \
- int __changed = !pte_same(huge_ptep_get(__ptep), __entry); \
- if (__changed) { \
- huge_ptep_invalidate((__vma)->vm_mm, __addr, __ptep); \
- set_huge_pte_at((__vma)->vm_mm, __addr, __ptep, __entry); \
- } \
- __changed; \
-})
-
-#define huge_ptep_set_wrprotect(__mm, __addr, __ptep) \
-({ \
- pte_t __pte = huge_ptep_get(__ptep); \
- if (huge_pte_write(__pte)) { \
- huge_ptep_invalidate(__mm, __addr, __ptep); \
- set_huge_pte_at(__mm, __addr, __ptep, \
- huge_pte_wrprotect(__pte)); \
- } \
-})
-
-static inline void huge_ptep_clear_flush(struct vm_area_struct *vma,
- unsigned long address, pte_t *ptep)
+static inline pte_t mk_huge_pte(struct page *page, pgprot_t pgprot)
{
- huge_ptep_invalidate(vma->vm_mm, address, ptep);
+ return mk_pte(page, pgprot);
}
-static inline pte_t mk_huge_pte(struct page *page, pgprot_t pgprot)
+static inline int huge_pte_none(pte_t pte)
{
- pte_t pte;
- pmd_t pmd;
-
- pmd = mk_pmd_phys(page_to_phys(page), pgprot);
- pte_val(pte) = pmd_val(pmd);
- return pte;
+ return pte_none(pte);
}
static inline int huge_pte_write(pte_t pte)
{
- pmd_t pmd;
-
- pmd_val(pmd) = pte_val(pte);
- return pmd_write(pmd);
+ return pte_write(pte);
}
static inline int huge_pte_dirty(pte_t pte)
{
- /* No dirty bit in the segment table entry. */
- return 0;
+ return pte_dirty(pte);
}
static inline pte_t huge_pte_mkwrite(pte_t pte)
{
- pmd_t pmd;
-
- pmd_val(pmd) = pte_val(pte);
- pte_val(pte) = pmd_val(pmd_mkwrite(pmd));
- return pte;
+ return pte_mkwrite(pte);
}
static inline pte_t huge_pte_mkdirty(pte_t pte)
{
- /* No dirty bit in the segment table entry. */
- return pte;
+ return pte_mkdirty(pte);
}
-static inline pte_t huge_pte_modify(pte_t pte, pgprot_t newprot)
+static inline pte_t huge_pte_wrprotect(pte_t pte)
{
- pmd_t pmd;
-
- pmd_val(pmd) = pte_val(pte);
- pte_val(pte) = pmd_val(pmd_modify(pmd, newprot));
- return pte;
+ return pte_wrprotect(pte);
}
-static inline void huge_pte_clear(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep)
+static inline pte_t huge_pte_modify(pte_t pte, pgprot_t newprot)
{
- pmd_clear((pmd_t *) ptep);
+ return pte_modify(pte, newprot);
}
#endif /* _ASM_S390_HUGETLB_H */
#include <linux/msi.h>
#include <linux/pci.h>
-static inline struct msi_desc *irq_get_msi_desc(unsigned int irq)
-{
- return __irq_get_msi_desc(irq);
-}
-
-/* Must be called with msi map lock held */
-static inline int irq_set_msi_desc(unsigned int irq, struct msi_desc *msi)
-{
- if (!msi)
- return -EINVAL;
-
- msi->irq = irq;
- return 0;
-}
+void __init init_airq_interrupts(void);
+void __init init_cio_interrupts(void);
+void __init init_ext_interrupts(void);
#endif
#ifndef _ASM_IRQ_H
#define _ASM_IRQ_H
+#define EXT_INTERRUPT 1
+#define IO_INTERRUPT 2
+#define THIN_INTERRUPT 3
+
+#define NR_IRQS_BASE 4
+
+#ifdef CONFIG_PCI_NR_MSI
+# define NR_IRQS (NR_IRQS_BASE + CONFIG_PCI_NR_MSI)
+#else
+# define NR_IRQS NR_IRQS_BASE
+#endif
+
+/* This number is used when no interrupt has been assigned */
+#define NO_IRQ 0
+
+#ifndef __ASSEMBLY__
+
#include <linux/hardirq.h>
#include <linux/percpu.h>
#include <linux/cache.h>
#include <linux/types.h>
-enum interruption_main_class {
- EXTERNAL_INTERRUPT,
- IO_INTERRUPT,
- NR_IRQS
-};
-
enum interruption_class {
IRQEXT_CLK,
IRQEXT_EXC,
void measurement_alert_subclass_register(void);
void measurement_alert_subclass_unregister(void);
-#ifdef CONFIG_LOCKDEP
-# define disable_irq_nosync_lockdep(irq) disable_irq_nosync(irq)
-# define disable_irq_nosync_lockdep_irqsave(irq, flags) \
- disable_irq_nosync(irq)
-# define disable_irq_lockdep(irq) disable_irq(irq)
-# define enable_irq_lockdep(irq) enable_irq(irq)
-# define enable_irq_lockdep_irqrestore(irq, flags) \
- enable_irq(irq)
-#endif
+#define irq_canonicalize(irq) (irq)
+
+#endif /* __ASSEMBLY__ */
#endif /* _ASM_IRQ_H */
WARN_ON(atomic_read(&prev->context.attach_count) < 0);
atomic_inc(&next->context.attach_count);
/* Check for TLBs not flushed yet */
- if (next->context.flush_mm)
- __tlb_flush_mm(next);
+ __tlb_flush_mm_lazy(next);
}
#define enter_lazy_tlb(mm,tsk) do { } while (0)
void storage_key_init_range(unsigned long start, unsigned long end);
-static inline unsigned long pfmf(unsigned long function, unsigned long address)
-{
- asm volatile(
- " .insn rre,0xb9af0000,%[function],%[address]"
- : [address] "+a" (address)
- : [function] "d" (function)
- : "memory");
- return address;
-}
-
static inline void clear_page(void *page)
{
register unsigned long reg1 asm ("1") = 0;
#define _PAGE_FP_BIT 0x08 /* HW fetch protection bit */
#define _PAGE_ACC_BITS 0xf0 /* HW access control bits */
-/*
- * Test and clear referenced bit in storage key.
- */
-#define __HAVE_ARCH_PAGE_TEST_AND_CLEAR_YOUNG
-static inline int page_test_and_clear_young(unsigned long pfn)
-{
- return page_reset_referenced(pfn << PAGE_SHIFT);
-}
-
struct page;
void arch_free_page(struct page *page, int order);
void arch_alloc_page(struct page *page, int order);
/* must be set before including pci_clp.h */
#define PCI_BAR_COUNT 6
+#include <linux/pci.h>
#include <asm-generic/pci.h>
#include <asm-generic/pci-dma-compat.h>
#include <asm/pci_clp.h>
atomic64_t unmapped_pages;
} __packed __aligned(16);
-struct msi_map {
- unsigned long irq;
- struct msi_desc *msi;
- struct hlist_node msi_chain;
-};
-
-#define ZPCI_NR_MSI_VECS 64
-#define ZPCI_MSI_MASK (ZPCI_NR_MSI_VECS - 1)
+#define ZPCI_MSI_VEC_BITS 11
+#define ZPCI_MSI_VEC_MAX (1 << ZPCI_MSI_VEC_BITS)
+#define ZPCI_MSI_VEC_MASK (ZPCI_MSI_VEC_MAX - 1)
enum zpci_state {
ZPCI_FN_STATE_RESERVED,
/* IRQ stuff */
u64 msi_addr; /* MSI address */
- struct zdev_irq_map *irq_map;
- struct msi_map *msi_map[ZPCI_NR_MSI_VECS];
+ struct airq_iv *aibv; /* adapter interrupt bit vector */
unsigned int aisb; /* number of the summary bit */
/* DMA stuff */
struct dentry *debugfs_perf;
};
-struct pci_hp_callback_ops {
- int (*create_slot) (struct zpci_dev *zdev);
- void (*remove_slot) (struct zpci_dev *zdev);
-};
-
static inline bool zdev_enabled(struct zpci_dev *zdev)
{
return (zdev->fh & (1UL << 31)) ? true : false;
int zpci_unregister_ioat(struct zpci_dev *, u8);
/* CLP */
-int clp_find_pci_devices(void);
+int clp_scan_pci_devices(void);
+int clp_rescan_pci_devices(void);
+int clp_rescan_pci_devices_simple(void);
int clp_add_pci_device(u32, u32, int);
int clp_enable_fh(struct zpci_dev *, u8);
int clp_disable_fh(struct zpci_dev *);
-/* MSI */
-struct msi_desc *__irq_get_msi_desc(unsigned int);
-int zpci_msi_set_mask_bits(struct msi_desc *, u32, u32);
-int zpci_setup_msi_irq(struct zpci_dev *, struct msi_desc *, unsigned int, int);
-void zpci_teardown_msi_irq(struct zpci_dev *, struct msi_desc *);
-int zpci_msihash_init(void);
-void zpci_msihash_exit(void);
-
#ifdef CONFIG_PCI
/* Error handling and recovery */
void zpci_event_error(void *);
void zpci_event_availability(void *);
+void zpci_rescan(void);
#else /* CONFIG_PCI */
static inline void zpci_event_error(void *e) {}
static inline void zpci_event_availability(void *e) {}
+static inline void zpci_rescan(void) {}
#endif /* CONFIG_PCI */
+#ifdef CONFIG_HOTPLUG_PCI_S390
+int zpci_init_slot(struct zpci_dev *);
+void zpci_exit_slot(struct zpci_dev *);
+#else /* CONFIG_HOTPLUG_PCI_S390 */
+static inline int zpci_init_slot(struct zpci_dev *zdev)
+{
+ return 0;
+}
+static inline void zpci_exit_slot(struct zpci_dev *zdev) {}
+#endif /* CONFIG_HOTPLUG_PCI_S390 */
+
/* Helpers */
struct zpci_dev *get_zdev(struct pci_dev *);
struct zpci_dev *get_zdev_by_fid(u32);
-bool zpci_fid_present(u32);
/* sysfs */
int zpci_sysfs_add_device(struct device *);
int zpci_dma_init(void);
void zpci_dma_exit(void);
-/* Hotplug */
-extern struct mutex zpci_list_lock;
-extern struct list_head zpci_list;
-extern unsigned int s390_pci_probe;
-
-void zpci_register_hp_ops(struct pci_hp_callback_ops *);
-void zpci_deregister_hp_ops(void);
-
/* FMB */
int zpci_fmb_enable_device(struct zpci_dev *);
int zpci_fmb_disable_device(struct zpci_dev *);
} __packed;
-int s390pci_mod_fc(u64 req, struct zpci_fib *fib);
-int s390pci_refresh_trans(u64 fn, u64 addr, u64 range);
-int s390pci_load(u64 *data, u64 req, u64 offset);
-int s390pci_store(u64 data, u64 req, u64 offset);
-int s390pci_store_block(const u64 *data, u64 req, u64 offset);
-void set_irq_ctrl(u16 ctl, char *unused, u8 isc);
+int zpci_mod_fc(u64 req, struct zpci_fib *fib);
+int zpci_refresh_trans(u64 fn, u64 addr, u64 range);
+int zpci_load(u64 *data, u64 req, u64 offset);
+int zpci_store(u64 data, u64 req, u64 offset);
+int zpci_store_block(const u64 *data, u64 req, u64 offset);
+void zpci_set_irq_ctrl(u16 ctl, char *unused, u8 isc);
#endif
u64 data; \
int rc; \
\
- rc = s390pci_load(&data, req, ZPCI_OFFSET(addr)); \
+ rc = zpci_load(&data, req, ZPCI_OFFSET(addr)); \
if (rc) \
data = -1ULL; \
return (RETTYPE) data; \
u64 req = ZPCI_CREATE_REQ(entry->fh, entry->bar, LENGTH); \
u64 data = (VALTYPE) val; \
\
- s390pci_store(data, req, ZPCI_OFFSET(addr)); \
+ zpci_store(data, req, ZPCI_OFFSET(addr)); \
}
zpci_read(8, u64)
val = 0; /* let FW report error */
break;
}
- return s390pci_store(val, req, offset);
+ return zpci_store(val, req, offset);
}
static inline int zpci_read_single(u64 req, u64 *dst, u64 offset, u8 len)
u64 data;
int cc;
- cc = s390pci_load(&data, req, offset);
+ cc = zpci_load(&data, req, offset);
if (cc)
goto out;
static inline int zpci_write_block(u64 req, const u64 *data, u64 offset)
{
- return s390pci_store_block(data, req, offset);
+ return zpci_store_block(data, req, offset);
}
static inline u8 zpci_get_max_write_size(u64 src, u64 dst, int len, int max)
/* Hardware bits in the page table entry */
#define _PAGE_CO 0x100 /* HW Change-bit override */
-#define _PAGE_RO 0x200 /* HW read-only bit */
+#define _PAGE_PROTECT 0x200 /* HW read-only bit */
#define _PAGE_INVALID 0x400 /* HW invalid bit */
+#define _PAGE_LARGE 0x800 /* Bit to mark a large pte */
/* Software bits in the page table entry */
-#define _PAGE_SWT 0x001 /* SW pte type bit t */
-#define _PAGE_SWX 0x002 /* SW pte type bit x */
-#define _PAGE_SWC 0x004 /* SW pte changed bit */
-#define _PAGE_SWR 0x008 /* SW pte referenced bit */
-#define _PAGE_SWW 0x010 /* SW pte write bit */
-#define _PAGE_SPECIAL 0x020 /* SW associated with special page */
+#define _PAGE_PRESENT 0x001 /* SW pte present bit */
+#define _PAGE_TYPE 0x002 /* SW pte type bit */
+#define _PAGE_YOUNG 0x004 /* SW pte young bit */
+#define _PAGE_DIRTY 0x008 /* SW pte dirty bit */
+#define _PAGE_READ 0x010 /* SW pte read bit */
+#define _PAGE_WRITE 0x020 /* SW pte write bit */
+#define _PAGE_SPECIAL 0x040 /* SW associated with special page */
#define __HAVE_ARCH_PTE_SPECIAL
/* Set of bits not changed in pte_modify */
#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_SPECIAL | _PAGE_CO | \
- _PAGE_SWC | _PAGE_SWR)
-
-/* Six different types of pages. */
-#define _PAGE_TYPE_EMPTY 0x400
-#define _PAGE_TYPE_NONE 0x401
-#define _PAGE_TYPE_SWAP 0x403
-#define _PAGE_TYPE_FILE 0x601 /* bit 0x002 is used for offset !! */
-#define _PAGE_TYPE_RO 0x200
-#define _PAGE_TYPE_RW 0x000
+ _PAGE_DIRTY | _PAGE_YOUNG)
/*
- * Only four types for huge pages, using the invalid bit and protection bit
- * of a segment table entry.
- */
-#define _HPAGE_TYPE_EMPTY 0x020 /* _SEGMENT_ENTRY_INV */
-#define _HPAGE_TYPE_NONE 0x220
-#define _HPAGE_TYPE_RO 0x200 /* _SEGMENT_ENTRY_RO */
-#define _HPAGE_TYPE_RW 0x000
-
-/*
- * PTE type bits are rather complicated. handle_pte_fault uses pte_present,
- * pte_none and pte_file to find out the pte type WITHOUT holding the page
- * table lock. ptep_clear_flush on the other hand uses ptep_clear_flush to
- * invalidate a given pte. ipte sets the hw invalid bit and clears all tlbs
- * for the page. The page table entry is set to _PAGE_TYPE_EMPTY afterwards.
- * This change is done while holding the lock, but the intermediate step
- * of a previously valid pte with the hw invalid bit set can be observed by
- * handle_pte_fault. That makes it necessary that all valid pte types with
- * the hw invalid bit set must be distinguishable from the four pte types
- * empty, none, swap and file.
+ * handle_pte_fault uses pte_present, pte_none and pte_file to find out the
+ * pte type WITHOUT holding the page table lock. The _PAGE_PRESENT bit
+ * is used to distinguish present from not-present ptes. It is changed only
+ * with the page table lock held.
+ *
+ * The following table gives the different possible bit combinations for
+ * the pte hardware and software bits in the last 12 bits of a pte:
*
- * irxt ipte irxt
- * _PAGE_TYPE_EMPTY 1000 -> 1000
- * _PAGE_TYPE_NONE 1001 -> 1001
- * _PAGE_TYPE_SWAP 1011 -> 1011
- * _PAGE_TYPE_FILE 11?1 -> 11?1
- * _PAGE_TYPE_RO 0100 -> 1100
- * _PAGE_TYPE_RW 0000 -> 1000
+ * 842100000000
+ * 000084210000
+ * 000000008421
+ * .IR...wrdytp
+ * empty .10...000000
+ * swap .10...xxxx10
+ * file .11...xxxxx0
+ * prot-none, clean, old .11...000001
+ * prot-none, clean, young .11...000101
+ * prot-none, dirty, old .10...001001
+ * prot-none, dirty, young .10...001101
+ * read-only, clean, old .11...010001
+ * read-only, clean, young .01...010101
+ * read-only, dirty, old .11...011001
+ * read-only, dirty, young .01...011101
+ * read-write, clean, old .11...110001
+ * read-write, clean, young .01...110101
+ * read-write, dirty, old .10...111001
+ * read-write, dirty, young .00...111101
*
- * pte_none is true for bits combinations 1000, 1010, 1100, 1110
- * pte_present is true for bits combinations 0000, 0010, 0100, 0110, 1001
- * pte_file is true for bits combinations 1101, 1111
- * swap pte is 1011 and 0001, 0011, 0101, 0111 are invalid.
+ * pte_present is true for the bit pattern .xx...xxxxx1, (pte & 0x001) == 0x001
+ * pte_none is true for the bit pattern .10...xxxx00, (pte & 0x603) == 0x400
+ * pte_file is true for the bit pattern .11...xxxxx0, (pte & 0x601) == 0x600
+ * pte_swap is true for the bit pattern .10...xxxx10, (pte & 0x603) == 0x402
*/
#ifndef CONFIG_64BIT
#define _ASCE_TABLE_LENGTH 0x7f /* 128 x 64 entries = 8k */
/* Bits in the segment table entry */
+#define _SEGMENT_ENTRY_BITS 0x7fffffffUL /* Valid segment table bits */
#define _SEGMENT_ENTRY_ORIGIN 0x7fffffc0UL /* page table origin */
-#define _SEGMENT_ENTRY_RO 0x200 /* page protection bit */
-#define _SEGMENT_ENTRY_INV 0x20 /* invalid segment table entry */
+#define _SEGMENT_ENTRY_PROTECT 0x200 /* page protection bit */
+#define _SEGMENT_ENTRY_INVALID 0x20 /* invalid segment table entry */
#define _SEGMENT_ENTRY_COMMON 0x10 /* common segment bit */
#define _SEGMENT_ENTRY_PTL 0x0f /* page table length */
+#define _SEGMENT_ENTRY_NONE _SEGMENT_ENTRY_PROTECT
#define _SEGMENT_ENTRY (_SEGMENT_ENTRY_PTL)
-#define _SEGMENT_ENTRY_EMPTY (_SEGMENT_ENTRY_INV)
+#define _SEGMENT_ENTRY_EMPTY (_SEGMENT_ENTRY_INVALID)
+
+/*
+ * Segment table entry encoding (I = invalid, R = read-only bit):
+ * ..R...I.....
+ * prot-none ..1...1.....
+ * read-only ..1...0.....
+ * read-write ..0...0.....
+ * empty ..0...1.....
+ */
/* Page status table bits for virtualization */
#define PGSTE_ACC_BITS 0xf0000000UL
#define PGSTE_HC_BIT 0x00200000UL
#define PGSTE_GR_BIT 0x00040000UL
#define PGSTE_GC_BIT 0x00020000UL
-#define PGSTE_UR_BIT 0x00008000UL
-#define PGSTE_UC_BIT 0x00004000UL /* user dirty (migration) */
-#define PGSTE_IN_BIT 0x00002000UL /* IPTE notify bit */
+#define PGSTE_IN_BIT 0x00008000UL /* IPTE notify bit */
#else /* CONFIG_64BIT */
/* Bits in the region table entry */
#define _REGION_ENTRY_ORIGIN ~0xfffUL/* region/segment table origin */
-#define _REGION_ENTRY_RO 0x200 /* region protection bit */
-#define _REGION_ENTRY_INV 0x20 /* invalid region table entry */
+#define _REGION_ENTRY_PROTECT 0x200 /* region protection bit */
+#define _REGION_ENTRY_INVALID 0x20 /* invalid region table entry */
#define _REGION_ENTRY_TYPE_MASK 0x0c /* region/segment table type mask */
#define _REGION_ENTRY_TYPE_R1 0x0c /* region first table type */
#define _REGION_ENTRY_TYPE_R2 0x08 /* region second table type */
#define _REGION_ENTRY_LENGTH 0x03 /* region third length */
#define _REGION1_ENTRY (_REGION_ENTRY_TYPE_R1 | _REGION_ENTRY_LENGTH)
-#define _REGION1_ENTRY_EMPTY (_REGION_ENTRY_TYPE_R1 | _REGION_ENTRY_INV)
+#define _REGION1_ENTRY_EMPTY (_REGION_ENTRY_TYPE_R1 | _REGION_ENTRY_INVALID)
#define _REGION2_ENTRY (_REGION_ENTRY_TYPE_R2 | _REGION_ENTRY_LENGTH)
-#define _REGION2_ENTRY_EMPTY (_REGION_ENTRY_TYPE_R2 | _REGION_ENTRY_INV)
+#define _REGION2_ENTRY_EMPTY (_REGION_ENTRY_TYPE_R2 | _REGION_ENTRY_INVALID)
#define _REGION3_ENTRY (_REGION_ENTRY_TYPE_R3 | _REGION_ENTRY_LENGTH)
-#define _REGION3_ENTRY_EMPTY (_REGION_ENTRY_TYPE_R3 | _REGION_ENTRY_INV)
+#define _REGION3_ENTRY_EMPTY (_REGION_ENTRY_TYPE_R3 | _REGION_ENTRY_INVALID)
#define _REGION3_ENTRY_LARGE 0x400 /* RTTE-format control, large page */
#define _REGION3_ENTRY_RO 0x200 /* page protection bit */
#define _REGION3_ENTRY_CO 0x100 /* change-recording override */
/* Bits in the segment table entry */
+#define _SEGMENT_ENTRY_BITS 0xfffffffffffffe33UL
+#define _SEGMENT_ENTRY_BITS_LARGE 0xfffffffffff1ff33UL
#define _SEGMENT_ENTRY_ORIGIN_LARGE ~0xfffffUL /* large page address */
#define _SEGMENT_ENTRY_ORIGIN ~0x7ffUL/* segment table origin */
-#define _SEGMENT_ENTRY_RO 0x200 /* page protection bit */
-#define _SEGMENT_ENTRY_INV 0x20 /* invalid segment table entry */
+#define _SEGMENT_ENTRY_PROTECT 0x200 /* page protection bit */
+#define _SEGMENT_ENTRY_INVALID 0x20 /* invalid segment table entry */
#define _SEGMENT_ENTRY (0)
-#define _SEGMENT_ENTRY_EMPTY (_SEGMENT_ENTRY_INV)
+#define _SEGMENT_ENTRY_EMPTY (_SEGMENT_ENTRY_INVALID)
#define _SEGMENT_ENTRY_LARGE 0x400 /* STE-format control, large page */
#define _SEGMENT_ENTRY_CO 0x100 /* change-recording override */
+#define _SEGMENT_ENTRY_SPLIT 0x001 /* THP splitting bit */
+#define _SEGMENT_ENTRY_YOUNG 0x002 /* SW segment young bit */
+#define _SEGMENT_ENTRY_NONE _SEGMENT_ENTRY_YOUNG
+
+/*
+ * Segment table entry encoding (R = read-only, I = invalid, y = young bit):
+ * ..R...I...y.
+ * prot-none, old ..0...1...1.
+ * prot-none, young ..1...1...1.
+ * read-only, old ..1...1...0.
+ * read-only, young ..1...0...1.
+ * read-write, old ..0...1...0.
+ * read-write, young ..0...0...1.
+ * The segment table origin is used to distinguish empty (origin==0) from
+ * read-write, old segment table entries (origin!=0)
+ */
+
#define _SEGMENT_ENTRY_SPLIT_BIT 0 /* THP splitting bit number */
-#define _SEGMENT_ENTRY_SPLIT (1UL << _SEGMENT_ENTRY_SPLIT_BIT)
/* Set of bits not changed in pmd_modify */
#define _SEGMENT_CHG_MASK (_SEGMENT_ENTRY_ORIGIN | _SEGMENT_ENTRY_LARGE \
#define PGSTE_HC_BIT 0x0020000000000000UL
#define PGSTE_GR_BIT 0x0004000000000000UL
#define PGSTE_GC_BIT 0x0002000000000000UL
-#define PGSTE_UR_BIT 0x0000800000000000UL
-#define PGSTE_UC_BIT 0x0000400000000000UL /* user dirty (migration) */
-#define PGSTE_IN_BIT 0x0000200000000000UL /* IPTE notify bit */
+#define PGSTE_IN_BIT 0x0000800000000000UL /* IPTE notify bit */
#endif /* CONFIG_64BIT */
/*
* Page protection definitions.
*/
-#define PAGE_NONE __pgprot(_PAGE_TYPE_NONE)
-#define PAGE_RO __pgprot(_PAGE_TYPE_RO)
-#define PAGE_RW __pgprot(_PAGE_TYPE_RO | _PAGE_SWW)
-#define PAGE_RWC __pgprot(_PAGE_TYPE_RW | _PAGE_SWW | _PAGE_SWC)
-
-#define PAGE_KERNEL PAGE_RWC
-#define PAGE_SHARED PAGE_KERNEL
-#define PAGE_COPY PAGE_RO
+#define PAGE_NONE __pgprot(_PAGE_PRESENT | _PAGE_INVALID)
+#define PAGE_READ __pgprot(_PAGE_PRESENT | _PAGE_READ | \
+ _PAGE_INVALID | _PAGE_PROTECT)
+#define PAGE_WRITE __pgprot(_PAGE_PRESENT | _PAGE_READ | _PAGE_WRITE | \
+ _PAGE_INVALID | _PAGE_PROTECT)
+
+#define PAGE_SHARED __pgprot(_PAGE_PRESENT | _PAGE_READ | _PAGE_WRITE | \
+ _PAGE_YOUNG | _PAGE_DIRTY)
+#define PAGE_KERNEL __pgprot(_PAGE_PRESENT | _PAGE_READ | _PAGE_WRITE | \
+ _PAGE_YOUNG | _PAGE_DIRTY)
+#define PAGE_KERNEL_RO __pgprot(_PAGE_PRESENT | _PAGE_READ | _PAGE_YOUNG | \
+ _PAGE_PROTECT)
/*
* On s390 the page table entry has an invalid bit and a read-only bit.
*/
/*xwr*/
#define __P000 PAGE_NONE
-#define __P001 PAGE_RO
-#define __P010 PAGE_RO
-#define __P011 PAGE_RO
-#define __P100 PAGE_RO
-#define __P101 PAGE_RO
-#define __P110 PAGE_RO
-#define __P111 PAGE_RO
+#define __P001 PAGE_READ
+#define __P010 PAGE_READ
+#define __P011 PAGE_READ
+#define __P100 PAGE_READ
+#define __P101 PAGE_READ
+#define __P110 PAGE_READ
+#define __P111 PAGE_READ
#define __S000 PAGE_NONE
-#define __S001 PAGE_RO
-#define __S010 PAGE_RW
-#define __S011 PAGE_RW
-#define __S100 PAGE_RO
-#define __S101 PAGE_RO
-#define __S110 PAGE_RW
-#define __S111 PAGE_RW
+#define __S001 PAGE_READ
+#define __S010 PAGE_WRITE
+#define __S011 PAGE_WRITE
+#define __S100 PAGE_READ
+#define __S101 PAGE_READ
+#define __S110 PAGE_WRITE
+#define __S111 PAGE_WRITE
/*
* Segment entry (large page) protection definitions.
*/
-#define SEGMENT_NONE __pgprot(_HPAGE_TYPE_NONE)
-#define SEGMENT_RO __pgprot(_HPAGE_TYPE_RO)
-#define SEGMENT_RW __pgprot(_HPAGE_TYPE_RW)
-
-static inline int mm_exclusive(struct mm_struct *mm)
-{
- return likely(mm == current->active_mm &&
- atomic_read(&mm->context.attach_count) <= 1);
-}
+#define SEGMENT_NONE __pgprot(_SEGMENT_ENTRY_INVALID | \
+ _SEGMENT_ENTRY_NONE)
+#define SEGMENT_READ __pgprot(_SEGMENT_ENTRY_INVALID | \
+ _SEGMENT_ENTRY_PROTECT)
+#define SEGMENT_WRITE __pgprot(_SEGMENT_ENTRY_INVALID)
static inline int mm_has_pgste(struct mm_struct *mm)
{
{
if ((pgd_val(pgd) & _REGION_ENTRY_TYPE_MASK) < _REGION_ENTRY_TYPE_R2)
return 0;
- return (pgd_val(pgd) & _REGION_ENTRY_INV) != 0UL;
+ return (pgd_val(pgd) & _REGION_ENTRY_INVALID) != 0UL;
}
static inline int pgd_bad(pgd_t pgd)
* invalid for either table entry.
*/
unsigned long mask =
- ~_SEGMENT_ENTRY_ORIGIN & ~_REGION_ENTRY_INV &
+ ~_SEGMENT_ENTRY_ORIGIN & ~_REGION_ENTRY_INVALID &
~_REGION_ENTRY_TYPE_MASK & ~_REGION_ENTRY_LENGTH;
return (pgd_val(pgd) & mask) != 0;
}
{
if ((pud_val(pud) & _REGION_ENTRY_TYPE_MASK) < _REGION_ENTRY_TYPE_R3)
return 0;
- return (pud_val(pud) & _REGION_ENTRY_INV) != 0UL;
+ return (pud_val(pud) & _REGION_ENTRY_INVALID) != 0UL;
}
static inline int pud_large(pud_t pud)
* invalid for either table entry.
*/
unsigned long mask =
- ~_SEGMENT_ENTRY_ORIGIN & ~_REGION_ENTRY_INV &
+ ~_SEGMENT_ENTRY_ORIGIN & ~_REGION_ENTRY_INVALID &
~_REGION_ENTRY_TYPE_MASK & ~_REGION_ENTRY_LENGTH;
return (pud_val(pud) & mask) != 0;
}
static inline int pmd_present(pmd_t pmd)
{
- unsigned long mask = _SEGMENT_ENTRY_INV | _SEGMENT_ENTRY_RO;
- return (pmd_val(pmd) & mask) == _HPAGE_TYPE_NONE ||
- !(pmd_val(pmd) & _SEGMENT_ENTRY_INV);
+ return pmd_val(pmd) != _SEGMENT_ENTRY_INVALID;
}
static inline int pmd_none(pmd_t pmd)
{
- return (pmd_val(pmd) & _SEGMENT_ENTRY_INV) &&
- !(pmd_val(pmd) & _SEGMENT_ENTRY_RO);
+ return pmd_val(pmd) == _SEGMENT_ENTRY_INVALID;
}
static inline int pmd_large(pmd_t pmd)
{
#ifdef CONFIG_64BIT
- return !!(pmd_val(pmd) & _SEGMENT_ENTRY_LARGE);
+ return (pmd_val(pmd) & _SEGMENT_ENTRY_LARGE) != 0;
#else
return 0;
#endif
}
+static inline int pmd_prot_none(pmd_t pmd)
+{
+ return (pmd_val(pmd) & _SEGMENT_ENTRY_INVALID) &&
+ (pmd_val(pmd) & _SEGMENT_ENTRY_NONE);
+}
+
static inline int pmd_bad(pmd_t pmd)
{
- unsigned long mask = ~_SEGMENT_ENTRY_ORIGIN & ~_SEGMENT_ENTRY_INV;
- return (pmd_val(pmd) & mask) != _SEGMENT_ENTRY;
+#ifdef CONFIG_64BIT
+ if (pmd_large(pmd))
+ return (pmd_val(pmd) & ~_SEGMENT_ENTRY_BITS_LARGE) != 0;
+#endif
+ return (pmd_val(pmd) & ~_SEGMENT_ENTRY_BITS) != 0;
}
#define __HAVE_ARCH_PMDP_SPLITTING_FLUSH
#define __HAVE_ARCH_PMD_WRITE
static inline int pmd_write(pmd_t pmd)
{
- return (pmd_val(pmd) & _SEGMENT_ENTRY_RO) == 0;
+ if (pmd_prot_none(pmd))
+ return 0;
+ return (pmd_val(pmd) & _SEGMENT_ENTRY_PROTECT) == 0;
}
static inline int pmd_young(pmd_t pmd)
{
- return 0;
+ int young = 0;
+#ifdef CONFIG_64BIT
+ if (pmd_prot_none(pmd))
+ young = (pmd_val(pmd) & _SEGMENT_ENTRY_PROTECT) != 0;
+ else
+ young = (pmd_val(pmd) & _SEGMENT_ENTRY_YOUNG) != 0;
+#endif
+ return young;
}
-static inline int pte_none(pte_t pte)
+static inline int pte_present(pte_t pte)
{
- return (pte_val(pte) & _PAGE_INVALID) && !(pte_val(pte) & _PAGE_SWT);
+ /* Bit pattern: (pte & 0x001) == 0x001 */
+ return (pte_val(pte) & _PAGE_PRESENT) != 0;
}
-static inline int pte_present(pte_t pte)
+static inline int pte_none(pte_t pte)
{
- unsigned long mask = _PAGE_RO | _PAGE_INVALID | _PAGE_SWT | _PAGE_SWX;
- return (pte_val(pte) & mask) == _PAGE_TYPE_NONE ||
- (!(pte_val(pte) & _PAGE_INVALID) &&
- !(pte_val(pte) & _PAGE_SWT));
+ /* Bit pattern: pte == 0x400 */
+ return pte_val(pte) == _PAGE_INVALID;
}
static inline int pte_file(pte_t pte)
{
- unsigned long mask = _PAGE_RO | _PAGE_INVALID | _PAGE_SWT;
- return (pte_val(pte) & mask) == _PAGE_TYPE_FILE;
+ /* Bit pattern: (pte & 0x601) == 0x600 */
+ return (pte_val(pte) & (_PAGE_INVALID | _PAGE_PROTECT | _PAGE_PRESENT))
+ == (_PAGE_INVALID | _PAGE_PROTECT);
}
static inline int pte_special(pte_t pte)
#endif
}
+static inline pgste_t pgste_get(pte_t *ptep)
+{
+ unsigned long pgste = 0;
+#ifdef CONFIG_PGSTE
+ pgste = *(unsigned long *)(ptep + PTRS_PER_PTE);
+#endif
+ return __pgste(pgste);
+}
+
static inline void pgste_set(pte_t *ptep, pgste_t pgste)
{
#ifdef CONFIG_PGSTE
static inline pgste_t pgste_update_all(pte_t *ptep, pgste_t pgste)
{
#ifdef CONFIG_PGSTE
- unsigned long address, bits;
- unsigned char skey;
+ unsigned long address, bits, skey;
if (pte_val(*ptep) & _PAGE_INVALID)
return pgste;
address = pte_val(*ptep) & PAGE_MASK;
- skey = page_get_storage_key(address);
+ skey = (unsigned long) page_get_storage_key(address);
bits = skey & (_PAGE_CHANGED | _PAGE_REFERENCED);
- /* Clear page changed & referenced bit in the storage key */
- if (bits & _PAGE_CHANGED)
+ if (!(pgste_val(pgste) & PGSTE_HC_BIT) && (bits & _PAGE_CHANGED)) {
+ /* Transfer dirty + referenced bit to host bits in pgste */
+ pgste_val(pgste) |= bits << 52;
page_set_storage_key(address, skey ^ bits, 0);
- else if (bits)
+ } else if (!(pgste_val(pgste) & PGSTE_HR_BIT) &&
+ (bits & _PAGE_REFERENCED)) {
+ /* Transfer referenced bit to host bit in pgste */
+ pgste_val(pgste) |= PGSTE_HR_BIT;
page_reset_referenced(address);
+ }
/* Transfer page changed & referenced bit to guest bits in pgste */
pgste_val(pgste) |= bits << 48; /* GR bit & GC bit */
- /* Get host changed & referenced bits from pgste */
- bits |= (pgste_val(pgste) & (PGSTE_HR_BIT | PGSTE_HC_BIT)) >> 52;
- /* Transfer page changed & referenced bit to kvm user bits */
- pgste_val(pgste) |= bits << 45; /* PGSTE_UR_BIT & PGSTE_UC_BIT */
- /* Clear relevant host bits in pgste. */
- pgste_val(pgste) &= ~(PGSTE_HR_BIT | PGSTE_HC_BIT);
- pgste_val(pgste) &= ~(PGSTE_ACC_BITS | PGSTE_FP_BIT);
/* Copy page access key and fetch protection bit to pgste */
- pgste_val(pgste) |=
- (unsigned long) (skey & (_PAGE_ACC_BITS | _PAGE_FP_BIT)) << 56;
- /* Transfer referenced bit to pte */
- pte_val(*ptep) |= (bits & _PAGE_REFERENCED) << 1;
+ pgste_val(pgste) &= ~(PGSTE_ACC_BITS | PGSTE_FP_BIT);
+ pgste_val(pgste) |= (skey & (_PAGE_ACC_BITS | _PAGE_FP_BIT)) << 56;
#endif
return pgste;
static inline pgste_t pgste_update_young(pte_t *ptep, pgste_t pgste)
{
#ifdef CONFIG_PGSTE
- int young;
-
if (pte_val(*ptep) & _PAGE_INVALID)
return pgste;
/* Get referenced bit from storage key */
- young = page_reset_referenced(pte_val(*ptep) & PAGE_MASK);
- if (young)
- pgste_val(pgste) |= PGSTE_GR_BIT;
- /* Get host referenced bit from pgste */
- if (pgste_val(pgste) & PGSTE_HR_BIT) {
- pgste_val(pgste) &= ~PGSTE_HR_BIT;
- young = 1;
- }
- /* Transfer referenced bit to kvm user bits and pte */
- if (young) {
- pgste_val(pgste) |= PGSTE_UR_BIT;
- pte_val(*ptep) |= _PAGE_SWR;
- }
+ if (page_reset_referenced(pte_val(*ptep) & PAGE_MASK))
+ pgste_val(pgste) |= PGSTE_HR_BIT | PGSTE_GR_BIT;
#endif
return pgste;
}
static inline void pgste_set_pte(pte_t *ptep, pte_t entry)
{
- if (!MACHINE_HAS_ESOP && (pte_val(entry) & _PAGE_SWW)) {
+ if (!MACHINE_HAS_ESOP && (pte_val(entry) & _PAGE_WRITE)) {
/*
* Without enhanced suppression-on-protection force
* the dirty bit on for all writable ptes.
*/
- pte_val(entry) |= _PAGE_SWC;
- pte_val(entry) &= ~_PAGE_RO;
+ pte_val(entry) |= _PAGE_DIRTY;
+ pte_val(entry) &= ~_PAGE_PROTECT;
}
*ptep = entry;
}
*/
static inline int pte_write(pte_t pte)
{
- return (pte_val(pte) & _PAGE_SWW) != 0;
+ return (pte_val(pte) & _PAGE_WRITE) != 0;
}
static inline int pte_dirty(pte_t pte)
{
- return (pte_val(pte) & _PAGE_SWC) != 0;
+ return (pte_val(pte) & _PAGE_DIRTY) != 0;
}
static inline int pte_young(pte_t pte)
{
-#ifdef CONFIG_PGSTE
- if (pte_val(pte) & _PAGE_SWR)
- return 1;
-#endif
- return 0;
+ return (pte_val(pte) & _PAGE_YOUNG) != 0;
}
/*
static inline void pmd_clear(pmd_t *pmdp)
{
- pmd_val(*pmdp) = _SEGMENT_ENTRY_EMPTY;
+ pmd_val(*pmdp) = _SEGMENT_ENTRY_INVALID;
}
static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
{
- pte_val(*ptep) = _PAGE_TYPE_EMPTY;
+ pte_val(*ptep) = _PAGE_INVALID;
}
/*
{
pte_val(pte) &= _PAGE_CHG_MASK;
pte_val(pte) |= pgprot_val(newprot);
- if ((pte_val(pte) & _PAGE_SWC) && (pte_val(pte) & _PAGE_SWW))
- pte_val(pte) &= ~_PAGE_RO;
+ /*
+ * newprot for PAGE_NONE, PAGE_READ and PAGE_WRITE has the
+ * invalid bit set, clear it again for readable, young pages
+ */
+ if ((pte_val(pte) & _PAGE_YOUNG) && (pte_val(pte) & _PAGE_READ))
+ pte_val(pte) &= ~_PAGE_INVALID;
+ /*
+ * newprot for PAGE_READ and PAGE_WRITE has the page protection
+ * bit set, clear it again for writable, dirty pages
+ */
+ if ((pte_val(pte) & _PAGE_DIRTY) && (pte_val(pte) & _PAGE_WRITE))
+ pte_val(pte) &= ~_PAGE_PROTECT;
return pte;
}
static inline pte_t pte_wrprotect(pte_t pte)
{
- pte_val(pte) &= ~_PAGE_SWW;
- /* Do not clobber _PAGE_TYPE_NONE pages! */
- if (!(pte_val(pte) & _PAGE_INVALID))
- pte_val(pte) |= _PAGE_RO;
+ pte_val(pte) &= ~_PAGE_WRITE;
+ pte_val(pte) |= _PAGE_PROTECT;
return pte;
}
static inline pte_t pte_mkwrite(pte_t pte)
{
- pte_val(pte) |= _PAGE_SWW;
- if (pte_val(pte) & _PAGE_SWC)
- pte_val(pte) &= ~_PAGE_RO;
+ pte_val(pte) |= _PAGE_WRITE;
+ if (pte_val(pte) & _PAGE_DIRTY)
+ pte_val(pte) &= ~_PAGE_PROTECT;
return pte;
}
static inline pte_t pte_mkclean(pte_t pte)
{
- pte_val(pte) &= ~_PAGE_SWC;
- /* Do not clobber _PAGE_TYPE_NONE pages! */
- if (!(pte_val(pte) & _PAGE_INVALID))
- pte_val(pte) |= _PAGE_RO;
+ pte_val(pte) &= ~_PAGE_DIRTY;
+ pte_val(pte) |= _PAGE_PROTECT;
return pte;
}
static inline pte_t pte_mkdirty(pte_t pte)
{
- pte_val(pte) |= _PAGE_SWC;
- if (pte_val(pte) & _PAGE_SWW)
- pte_val(pte) &= ~_PAGE_RO;
+ pte_val(pte) |= _PAGE_DIRTY;
+ if (pte_val(pte) & _PAGE_WRITE)
+ pte_val(pte) &= ~_PAGE_PROTECT;
return pte;
}
static inline pte_t pte_mkold(pte_t pte)
{
-#ifdef CONFIG_PGSTE
- pte_val(pte) &= ~_PAGE_SWR;
-#endif
+ pte_val(pte) &= ~_PAGE_YOUNG;
+ pte_val(pte) |= _PAGE_INVALID;
return pte;
}
static inline pte_t pte_mkyoung(pte_t pte)
{
+ pte_val(pte) |= _PAGE_YOUNG;
+ if (pte_val(pte) & _PAGE_READ)
+ pte_val(pte) &= ~_PAGE_INVALID;
return pte;
}
#ifdef CONFIG_HUGETLB_PAGE
static inline pte_t pte_mkhuge(pte_t pte)
{
- pte_val(pte) |= (_SEGMENT_ENTRY_LARGE | _SEGMENT_ENTRY_CO);
+ pte_val(pte) |= _PAGE_LARGE;
return pte;
}
#endif
if (mm_has_pgste(mm)) {
pgste = pgste_get_lock(ptep);
pgste = pgste_update_all(ptep, pgste);
- dirty = !!(pgste_val(pgste) & PGSTE_UC_BIT);
- pgste_val(pgste) &= ~PGSTE_UC_BIT;
+ dirty = !!(pgste_val(pgste) & PGSTE_HC_BIT);
+ pgste_val(pgste) &= ~PGSTE_HC_BIT;
pgste_set_unlock(ptep, pgste);
return dirty;
}
if (mm_has_pgste(mm)) {
pgste = pgste_get_lock(ptep);
pgste = pgste_update_young(ptep, pgste);
- young = !!(pgste_val(pgste) & PGSTE_UR_BIT);
- pgste_val(pgste) &= ~PGSTE_UR_BIT;
+ young = !!(pgste_val(pgste) & PGSTE_HR_BIT);
+ pgste_val(pgste) &= ~PGSTE_HR_BIT;
pgste_set_unlock(ptep, pgste);
}
return young;
}
+static inline void __ptep_ipte(unsigned long address, pte_t *ptep)
+{
+ if (!(pte_val(*ptep) & _PAGE_INVALID)) {
+#ifndef CONFIG_64BIT
+ /* pto must point to the start of the segment table */
+ pte_t *pto = (pte_t *) (((unsigned long) ptep) & 0x7ffffc00);
+#else
+ /* ipte in zarch mode can do the math */
+ pte_t *pto = ptep;
+#endif
+ asm volatile(
+ " ipte %2,%3"
+ : "=m" (*ptep) : "m" (*ptep),
+ "a" (pto), "a" (address));
+ }
+}
+
+static inline void ptep_flush_lazy(struct mm_struct *mm,
+ unsigned long address, pte_t *ptep)
+{
+ int active = (mm == current->active_mm) ? 1 : 0;
+
+ if (atomic_read(&mm->context.attach_count) > active)
+ __ptep_ipte(address, ptep);
+ else
+ mm->context.flush_mm = 1;
+}
+
#define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG
static inline int ptep_test_and_clear_young(struct vm_area_struct *vma,
unsigned long addr, pte_t *ptep)
{
pgste_t pgste;
pte_t pte;
+ int young;
if (mm_has_pgste(vma->vm_mm)) {
pgste = pgste_get_lock(ptep);
- pgste = pgste_update_young(ptep, pgste);
- pte = *ptep;
- *ptep = pte_mkold(pte);
- pgste_set_unlock(ptep, pgste);
- return pte_young(pte);
+ pgste = pgste_ipte_notify(vma->vm_mm, addr, ptep, pgste);
}
- return 0;
+
+ pte = *ptep;
+ __ptep_ipte(addr, ptep);
+ young = pte_young(pte);
+ pte = pte_mkold(pte);
+
+ if (mm_has_pgste(vma->vm_mm)) {
+ pgste_set_pte(ptep, pte);
+ pgste_set_unlock(ptep, pgste);
+ } else
+ *ptep = pte;
+
+ return young;
}
#define __HAVE_ARCH_PTEP_CLEAR_YOUNG_FLUSH
static inline int ptep_clear_flush_young(struct vm_area_struct *vma,
unsigned long address, pte_t *ptep)
{
- /* No need to flush TLB
- * On s390 reference bits are in storage key and never in TLB
- * With virtualization we handle the reference bit, without we
- * we can simply return */
return ptep_test_and_clear_young(vma, address, ptep);
}
-static inline void __ptep_ipte(unsigned long address, pte_t *ptep)
-{
- if (!(pte_val(*ptep) & _PAGE_INVALID)) {
-#ifndef CONFIG_64BIT
- /* pto must point to the start of the segment table */
- pte_t *pto = (pte_t *) (((unsigned long) ptep) & 0x7ffffc00);
-#else
- /* ipte in zarch mode can do the math */
- pte_t *pto = ptep;
-#endif
- asm volatile(
- " ipte %2,%3"
- : "=m" (*ptep) : "m" (*ptep),
- "a" (pto), "a" (address));
- }
-}
-
/*
* This is hard to understand. ptep_get_and_clear and ptep_clear_flush
* both clear the TLB for the unmapped pte. The reason is that
pgste_t pgste;
pte_t pte;
- mm->context.flush_mm = 1;
if (mm_has_pgste(mm)) {
pgste = pgste_get_lock(ptep);
pgste = pgste_ipte_notify(mm, address, ptep, pgste);
}
pte = *ptep;
- if (!mm_exclusive(mm))
- __ptep_ipte(address, ptep);
- pte_val(*ptep) = _PAGE_TYPE_EMPTY;
+ ptep_flush_lazy(mm, address, ptep);
+ pte_val(*ptep) = _PAGE_INVALID;
if (mm_has_pgste(mm)) {
pgste = pgste_update_all(&pte, pgste);
pgste_t pgste;
pte_t pte;
- mm->context.flush_mm = 1;
if (mm_has_pgste(mm)) {
pgste = pgste_get_lock(ptep);
pgste_ipte_notify(mm, address, ptep, pgste);
}
pte = *ptep;
- if (!mm_exclusive(mm))
- __ptep_ipte(address, ptep);
+ ptep_flush_lazy(mm, address, ptep);
+ pte_val(*ptep) |= _PAGE_INVALID;
if (mm_has_pgste(mm)) {
pgste = pgste_update_all(&pte, pgste);
pgste_t pgste;
if (mm_has_pgste(mm)) {
- pgste = *(pgste_t *)(ptep + PTRS_PER_PTE);
+ pgste = pgste_get(ptep);
pgste_set_key(ptep, pgste, pte);
pgste_set_pte(ptep, pte);
pgste_set_unlock(ptep, pgste);
pte = *ptep;
__ptep_ipte(address, ptep);
- pte_val(*ptep) = _PAGE_TYPE_EMPTY;
+ pte_val(*ptep) = _PAGE_INVALID;
if (mm_has_pgste(vma->vm_mm)) {
pgste = pgste_update_all(&pte, pgste);
pgste_t pgste;
pte_t pte;
- if (mm_has_pgste(mm)) {
+ if (!full && mm_has_pgste(mm)) {
pgste = pgste_get_lock(ptep);
- if (!full)
- pgste = pgste_ipte_notify(mm, address, ptep, pgste);
+ pgste = pgste_ipte_notify(mm, address, ptep, pgste);
}
pte = *ptep;
if (!full)
- __ptep_ipte(address, ptep);
- pte_val(*ptep) = _PAGE_TYPE_EMPTY;
+ ptep_flush_lazy(mm, address, ptep);
+ pte_val(*ptep) = _PAGE_INVALID;
- if (mm_has_pgste(mm)) {
+ if (!full && mm_has_pgste(mm)) {
pgste = pgste_update_all(&pte, pgste);
pgste_set_unlock(ptep, pgste);
}
pte_t pte = *ptep;
if (pte_write(pte)) {
- mm->context.flush_mm = 1;
if (mm_has_pgste(mm)) {
pgste = pgste_get_lock(ptep);
pgste = pgste_ipte_notify(mm, address, ptep, pgste);
}
- if (!mm_exclusive(mm))
- __ptep_ipte(address, ptep);
+ ptep_flush_lazy(mm, address, ptep);
pte = pte_wrprotect(pte);
if (mm_has_pgste(mm)) {
{
pte_t __pte;
pte_val(__pte) = physpage + pgprot_val(pgprot);
- return __pte;
+ return pte_mkyoung(__pte);
}
static inline pte_t mk_pte(struct page *page, pgprot_t pgprot)
unsigned long physpage = page_to_phys(page);
pte_t __pte = mk_pte_phys(physpage, pgprot);
- if ((pte_val(__pte) & _PAGE_SWW) && PageDirty(page)) {
- pte_val(__pte) |= _PAGE_SWC;
- pte_val(__pte) &= ~_PAGE_RO;
- }
+ if (pte_write(__pte) && PageDirty(page))
+ __pte = pte_mkdirty(__pte);
return __pte;
}
unsigned long sto = (unsigned long) pmdp -
pmd_index(address) * sizeof(pmd_t);
- if (!(pmd_val(*pmdp) & _SEGMENT_ENTRY_INV)) {
+ if (!(pmd_val(*pmdp) & _SEGMENT_ENTRY_INVALID)) {
asm volatile(
" .insn rrf,0xb98e0000,%2,%3,0,0"
: "=m" (*pmdp)
}
}
+static inline void __pmd_csp(pmd_t *pmdp)
+{
+ register unsigned long reg2 asm("2") = pmd_val(*pmdp);
+ register unsigned long reg3 asm("3") = pmd_val(*pmdp) |
+ _SEGMENT_ENTRY_INVALID;
+ register unsigned long reg4 asm("4") = ((unsigned long) pmdp) + 5;
+
+ asm volatile(
+ " csp %1,%3"
+ : "=m" (*pmdp)
+ : "d" (reg2), "d" (reg3), "d" (reg4), "m" (*pmdp) : "cc");
+}
+
#if defined(CONFIG_TRANSPARENT_HUGEPAGE) || defined(CONFIG_HUGETLB_PAGE)
static inline unsigned long massage_pgprot_pmd(pgprot_t pgprot)
{
/*
- * pgprot is PAGE_NONE, PAGE_RO, or PAGE_RW (see __Pxxx / __Sxxx)
+ * pgprot is PAGE_NONE, PAGE_READ, or PAGE_WRITE (see __Pxxx / __Sxxx)
* Convert to segment table entry format.
*/
if (pgprot_val(pgprot) == pgprot_val(PAGE_NONE))
return pgprot_val(SEGMENT_NONE);
- if (pgprot_val(pgprot) == pgprot_val(PAGE_RO))
- return pgprot_val(SEGMENT_RO);
- return pgprot_val(SEGMENT_RW);
+ if (pgprot_val(pgprot) == pgprot_val(PAGE_READ))
+ return pgprot_val(SEGMENT_READ);
+ return pgprot_val(SEGMENT_WRITE);
+}
+
+static inline pmd_t pmd_mkyoung(pmd_t pmd)
+{
+#ifdef CONFIG_64BIT
+ if (pmd_prot_none(pmd)) {
+ pmd_val(pmd) |= _SEGMENT_ENTRY_PROTECT;
+ } else {
+ pmd_val(pmd) |= _SEGMENT_ENTRY_YOUNG;
+ pmd_val(pmd) &= ~_SEGMENT_ENTRY_INVALID;
+ }
+#endif
+ return pmd;
+}
+
+static inline pmd_t pmd_mkold(pmd_t pmd)
+{
+#ifdef CONFIG_64BIT
+ if (pmd_prot_none(pmd)) {
+ pmd_val(pmd) &= ~_SEGMENT_ENTRY_PROTECT;
+ } else {
+ pmd_val(pmd) &= ~_SEGMENT_ENTRY_YOUNG;
+ pmd_val(pmd) |= _SEGMENT_ENTRY_INVALID;
+ }
+#endif
+ return pmd;
}
static inline pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot)
{
+ int young;
+
+ young = pmd_young(pmd);
pmd_val(pmd) &= _SEGMENT_CHG_MASK;
pmd_val(pmd) |= massage_pgprot_pmd(newprot);
+ if (young)
+ pmd = pmd_mkyoung(pmd);
return pmd;
}
{
pmd_t __pmd;
pmd_val(__pmd) = physpage + massage_pgprot_pmd(pgprot);
- return __pmd;
+ return pmd_mkyoung(__pmd);
}
static inline pmd_t pmd_mkwrite(pmd_t pmd)
{
- /* Do not clobber _HPAGE_TYPE_NONE pages! */
- if (!(pmd_val(pmd) & _SEGMENT_ENTRY_INV))
- pmd_val(pmd) &= ~_SEGMENT_ENTRY_RO;
+ /* Do not clobber PROT_NONE segments! */
+ if (!pmd_prot_none(pmd))
+ pmd_val(pmd) &= ~_SEGMENT_ENTRY_PROTECT;
return pmd;
}
#endif /* CONFIG_TRANSPARENT_HUGEPAGE || CONFIG_HUGETLB_PAGE */
static inline void set_pmd_at(struct mm_struct *mm, unsigned long addr,
pmd_t *pmdp, pmd_t entry)
{
- if (!(pmd_val(entry) & _SEGMENT_ENTRY_INV) && MACHINE_HAS_EDAT1)
+ if (!(pmd_val(entry) & _SEGMENT_ENTRY_INVALID) && MACHINE_HAS_EDAT1)
pmd_val(entry) |= _SEGMENT_ENTRY_CO;
*pmdp = entry;
}
static inline pmd_t pmd_wrprotect(pmd_t pmd)
{
- pmd_val(pmd) |= _SEGMENT_ENTRY_RO;
+ /* Do not clobber PROT_NONE segments! */
+ if (!pmd_prot_none(pmd))
+ pmd_val(pmd) |= _SEGMENT_ENTRY_PROTECT;
return pmd;
}
return pmd;
}
-static inline pmd_t pmd_mkold(pmd_t pmd)
-{
- /* No referenced bit in the segment table entry. */
- return pmd;
-}
-
-static inline pmd_t pmd_mkyoung(pmd_t pmd)
-{
- /* No referenced bit in the segment table entry. */
- return pmd;
-}
-
#define __HAVE_ARCH_PMDP_TEST_AND_CLEAR_YOUNG
static inline int pmdp_test_and_clear_young(struct vm_area_struct *vma,
unsigned long address, pmd_t *pmdp)
{
- unsigned long pmd_addr = pmd_val(*pmdp) & HPAGE_MASK;
- long tmp, rc;
- int counter;
+ pmd_t pmd;
- rc = 0;
- if (MACHINE_HAS_RRBM) {
- counter = PTRS_PER_PTE >> 6;
- asm volatile(
- "0: .insn rre,0xb9ae0000,%0,%3\n" /* rrbm */
- " ogr %1,%0\n"
- " la %3,0(%4,%3)\n"
- " brct %2,0b\n"
- : "=&d" (tmp), "+&d" (rc), "+d" (counter),
- "+a" (pmd_addr)
- : "a" (64 * 4096UL) : "cc");
- rc = !!rc;
- } else {
- counter = PTRS_PER_PTE;
- asm volatile(
- "0: rrbe 0,%2\n"
- " la %2,0(%3,%2)\n"
- " brc 12,1f\n"
- " lhi %0,1\n"
- "1: brct %1,0b\n"
- : "+d" (rc), "+d" (counter), "+a" (pmd_addr)
- : "a" (4096UL) : "cc");
- }
- return rc;
+ pmd = *pmdp;
+ __pmd_idte(address, pmdp);
+ *pmdp = pmd_mkold(pmd);
+ return pmd_young(pmd);
}
#define __HAVE_ARCH_PMDP_GET_AND_CLEAR
* exception will occur instead of a page translation exception. The
* specifiation exception has the bad habit not to store necessary
* information in the lowcore.
- * Bit 21 and bit 22 are the page invalid bit and the page protection
- * bit. We set both to indicate a swapped page.
- * Bit 30 and 31 are used to distinguish the different page types. For
- * a swapped page these bits need to be zero.
+ * Bits 21, 22, 30 and 31 are used to indicate the page type.
+ * A swap pte is indicated by bit pattern (pte & 0x603) == 0x402
* This leaves the bits 1-19 and bits 24-29 to store type and offset.
* We use the 5 bits from 25-29 for the type and the 20 bits from 1-19
* plus 24 for the offset.
* exception will occur instead of a page translation exception. The
* specifiation exception has the bad habit not to store necessary
* information in the lowcore.
- * Bit 53 and bit 54 are the page invalid bit and the page protection
- * bit. We set both to indicate a swapped page.
- * Bit 62 and 63 are used to distinguish the different page types. For
- * a swapped page these bits need to be zero.
+ * Bits 53, 54, 62 and 63 are used to indicate the page type.
+ * A swap pte is indicated by bit pattern (pte & 0x603) == 0x402
* This leaves the bits 0-51 and bits 56-61 to store type and offset.
* We use the 5 bits from 57-61 for the type and the 53 bits from 0-51
* plus 56 for the offset.
{
pte_t pte;
offset &= __SWP_OFFSET_MASK;
- pte_val(pte) = _PAGE_TYPE_SWAP | ((type & 0x1f) << 2) |
+ pte_val(pte) = _PAGE_INVALID | _PAGE_TYPE | ((type & 0x1f) << 2) |
((offset & 1UL) << 7) | ((offset & ~1UL) << 11);
return pte;
}
#define pgoff_to_pte(__off) \
((pte_t) { ((((__off) & 0x7f) << 1) + (((__off) >> 7) << 12)) \
- | _PAGE_TYPE_FILE })
+ | _PAGE_INVALID | _PAGE_PROTECT })
#endif /* !__ASSEMBLY__ */
--- /dev/null
+#ifndef _ASM_S390_SERIAL_H
+#define _ASM_S390_SERIAL_H
+
+#define BASE_BAUD 0
+
+#endif /* _ASM_S390_SERIAL_H */
#define __ASM_SWITCH_TO_H
#include <linux/thread_info.h>
+#include <asm/ptrace.h>
extern struct task_struct *__switch_to(void *, void *);
extern void update_cr_regs(struct task_struct *task);
static inline void save_access_regs(unsigned int *acrs)
{
- asm volatile("stam 0,15,%0" : "=Q" (*acrs));
+ typedef struct { int _[NUM_ACRS]; } acrstype;
+
+ asm volatile("stam 0,15,%0" : "=Q" (*(acrstype *)acrs));
}
static inline void restore_access_regs(unsigned int *acrs)
{
- asm volatile("lam 0,15,%0" : : "Q" (*acrs));
+ typedef struct { int _[NUM_ACRS]; } acrstype;
+
+ asm volatile("lam 0,15,%0" : : "Q" (*(acrstype *)acrs));
}
#define switch_to(prev,next,last) do { \
static inline void tlb_flush_mmu(struct mmu_gather *tlb)
{
+ __tlb_flush_mm_lazy(tlb->mm);
tlb_table_flush(tlb);
}
static inline void tlb_finish_mmu(struct mmu_gather *tlb,
unsigned long start, unsigned long end)
{
- tlb_table_flush(tlb);
+ tlb_flush_mmu(tlb);
}
/*
__tlb_flush_full(mm);
}
-static inline void __tlb_flush_mm_cond(struct mm_struct * mm)
+static inline void __tlb_flush_mm_lazy(struct mm_struct * mm)
{
if (mm->context.flush_mm) {
__tlb_flush_mm(mm);
static inline void flush_tlb_mm(struct mm_struct *mm)
{
- __tlb_flush_mm_cond(mm);
+ __tlb_flush_mm_lazy(mm);
}
static inline void flush_tlb_range(struct vm_area_struct *vma,
unsigned long start, unsigned long end)
{
- __tlb_flush_mm_cond(vma->vm_mm);
+ __tlb_flush_mm_lazy(vma->vm_mm);
}
static inline void flush_tlb_kernel_range(unsigned long start,
#include <asm/unistd.h>
#include <asm/page.h>
#include <asm/sigp.h>
+#include <asm/irq.h>
__PT_R0 = __PT_GPRS
__PT_R1 = __PT_GPRS + 4
io_loop:
l %r1,BASED(.Ldo_IRQ)
lr %r2,%r11 # pass pointer to pt_regs
+ lhi %r3,IO_INTERRUPT
+ tm __PT_INT_CODE+8(%r11),0x80 # adapter interrupt ?
+ jz io_call
+ lhi %r3,THIN_INTERRUPT
+io_call:
basr %r14,%r1 # call do_IRQ
tm __LC_MACHINE_FLAGS+2,0x10 # MACHINE_FLAG_LPAR
jz io_return
mvc __PT_INT_CODE(4,%r11),__LC_EXT_CPU_ADDR
mvc __PT_INT_PARM(4,%r11),__LC_EXT_PARAMS
TRACE_IRQS_OFF
+ l %r1,BASED(.Ldo_IRQ)
lr %r2,%r11 # pass pointer to pt_regs
- l %r1,BASED(.Ldo_extint)
- basr %r14,%r1 # call do_extint
+ lhi %r3,EXT_INTERRUPT
+ basr %r14,%r1 # call do_IRQ
j io_return
/*
stm %r9,%r10,__LC_SYSTEM_TIMER
mvc __LC_LAST_UPDATE_TIMER(8),__TIMER_IDLE_EXIT(%r2)
# prepare return psw
- n %r8,BASED(cleanup_idle_wait) # clear wait state bit
+ n %r8,BASED(cleanup_idle_wait) # clear irq & wait state bits
l %r9,24(%r11) # return from psw_idle
br %r14
cleanup_idle_insn:
.long psw_idle_lpsw + 0x80000000
cleanup_idle_wait:
- .long 0xfffdffff
+ .long 0xfcfdffff
/*
* Integer constants
.Ldo_machine_check: .long s390_do_machine_check
.Lhandle_mcck: .long s390_handle_mcck
.Ldo_IRQ: .long do_IRQ
-.Ldo_extint: .long do_extint
.Ldo_signal: .long do_signal
.Ldo_notify_resume: .long do_notify_resume
.Ldo_per_trap: .long do_per_trap
#include <asm/unistd.h>
#include <asm/page.h>
#include <asm/sigp.h>
+#include <asm/irq.h>
__PT_R0 = __PT_GPRS
__PT_R1 = __PT_GPRS + 8
xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
io_loop:
lgr %r2,%r11 # pass pointer to pt_regs
+ lghi %r3,IO_INTERRUPT
+ tm __PT_INT_CODE+8(%r11),0x80 # adapter interrupt ?
+ jz io_call
+ lghi %r3,THIN_INTERRUPT
+io_call:
brasl %r14,do_IRQ
tm __LC_MACHINE_FLAGS+6,0x10 # MACHINE_FLAG_LPAR
jz io_return
TRACE_IRQS_OFF
xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
lgr %r2,%r11 # pass pointer to pt_regs
- brasl %r14,do_extint
+ lghi %r3,EXT_INTERRUPT
+ brasl %r14,do_IRQ
j io_return
/*
stg %r9,__LC_SYSTEM_TIMER
mvc __LC_LAST_UPDATE_TIMER(8),__TIMER_IDLE_EXIT(%r2)
# prepare return psw
- nihh %r8,0xfffd # clear wait state bit
+ nihh %r8,0xfcfd # clear irq & wait state bits
lg %r9,48(%r11) # return from psw_idle
br %r14
cleanup_idle_insn:
#include <asm/cputime.h>
#include <asm/lowcore.h>
#include <asm/irq.h>
+#include <asm/hw_irq.h>
#include "entry.h"
DEFINE_PER_CPU_SHARED_ALIGNED(struct irq_stat, irq_stat);
* Since the external and I/O interrupt fields are already sums we would end
* up with having a sum which accounts each interrupt twice.
*/
-static const struct irq_class irqclass_main_desc[NR_IRQS] = {
- [EXTERNAL_INTERRUPT] = {.name = "EXT"},
- [IO_INTERRUPT] = {.name = "I/O"}
+static const struct irq_class irqclass_main_desc[NR_IRQS_BASE] = {
+ [EXT_INTERRUPT] = {.name = "EXT"},
+ [IO_INTERRUPT] = {.name = "I/O"},
+ [THIN_INTERRUPT] = {.name = "AIO"},
};
/*
[CPU_RST] = {.name = "RST", .desc = "[CPU] CPU Restart"},
};
+void __init init_IRQ(void)
+{
+ irq_reserve_irqs(0, THIN_INTERRUPT);
+ init_cio_interrupts();
+ init_airq_interrupts();
+ init_ext_interrupts();
+}
+
+void do_IRQ(struct pt_regs *regs, int irq)
+{
+ struct pt_regs *old_regs;
+
+ old_regs = set_irq_regs(regs);
+ irq_enter();
+ if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator)
+ /* Serve timer interrupts first. */
+ clock_comparator_work();
+ generic_handle_irq(irq);
+ irq_exit();
+ set_irq_regs(old_regs);
+}
+
/*
* show_interrupts is needed by /proc/interrupts.
*/
for_each_online_cpu(cpu)
seq_printf(p, "CPU%d ", cpu);
seq_putc(p, '\n');
+ goto out;
}
if (irq < NR_IRQS) {
+ if (irq >= NR_IRQS_BASE)
+ goto out;
seq_printf(p, "%s: ", irqclass_main_desc[irq].name);
for_each_online_cpu(cpu)
- seq_printf(p, "%10u ", kstat_cpu(cpu).irqs[irq]);
+ seq_printf(p, "%10u ", kstat_irqs_cpu(irq, cpu));
seq_putc(p, '\n');
- goto skip_arch_irqs;
+ goto out;
}
for (irq = 0; irq < NR_ARCH_IRQS; irq++) {
seq_printf(p, "%s: ", irqclass_sub_desc[irq].name);
for_each_online_cpu(cpu)
- seq_printf(p, "%10u ", per_cpu(irq_stat, cpu).irqs[irq]);
+ seq_printf(p, "%10u ",
+ per_cpu(irq_stat, cpu).irqs[irq]);
if (irqclass_sub_desc[irq].desc)
seq_printf(p, " %s", irqclass_sub_desc[irq].desc);
seq_putc(p, '\n');
}
-skip_arch_irqs:
+out:
put_online_cpus();
return 0;
}
+int arch_show_interrupts(struct seq_file *p, int prec)
+{
+ return 0;
+}
+
/*
* Switch to the asynchronous interrupt stack for softirq execution.
*/
local_irq_restore(flags);
}
-#ifdef CONFIG_PROC_FS
-void init_irq_proc(void)
-{
- if (proc_mkdir("irq", NULL))
- create_prof_cpu_mask();
-}
-#endif
-
/*
* ext_int_hash[index] is the list head for all external interrupts that hash
* to this index.
/* ext_int_hash_lock protects the handler lists for external interrupts */
DEFINE_SPINLOCK(ext_int_hash_lock);
-static void __init init_external_interrupts(void)
-{
- int idx;
-
- for (idx = 0; idx < ARRAY_SIZE(ext_int_hash); idx++)
- INIT_LIST_HEAD(&ext_int_hash[idx]);
-}
-
static inline int ext_hash(u16 code)
{
return (code + (code >> 9)) & 0xff;
}
EXPORT_SYMBOL(unregister_external_interrupt);
-void __irq_entry do_extint(struct pt_regs *regs)
+static irqreturn_t do_ext_interrupt(int irq, void *dummy)
{
+ struct pt_regs *regs = get_irq_regs();
struct ext_code ext_code;
- struct pt_regs *old_regs;
struct ext_int_info *p;
int index;
- old_regs = set_irq_regs(regs);
- irq_enter();
- if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator) {
- /* Serve timer interrupts first. */
- clock_comparator_work();
- }
- kstat_incr_irqs_this_cpu(EXTERNAL_INTERRUPT, NULL);
ext_code = *(struct ext_code *) ®s->int_code;
if (ext_code.code != 0x1004)
__get_cpu_var(s390_idle).nohz_delay = 1;
p->handler(ext_code, regs->int_parm,
regs->int_parm_long);
rcu_read_unlock();
- irq_exit();
- set_irq_regs(old_regs);
+
+ return IRQ_HANDLED;
}
-void __init init_IRQ(void)
+static struct irqaction external_interrupt = {
+ .name = "EXT",
+ .handler = do_ext_interrupt,
+};
+
+void __init init_ext_interrupts(void)
{
- init_external_interrupts();
+ int idx;
+
+ for (idx = 0; idx < ARRAY_SIZE(ext_int_hash); idx++)
+ INIT_LIST_HEAD(&ext_int_hash[idx]);
+
+ irq_set_chip_and_handler(EXT_INTERRUPT,
+ &dummy_irq_chip, handle_percpu_irq);
+ setup_irq(EXT_INTERRUPT, &external_interrupt);
}
static DEFINE_SPINLOCK(sc_irq_lock);
spin_unlock(&ma_subclass_lock);
}
EXPORT_SYMBOL(measurement_alert_subclass_unregister);
-
-#ifdef CONFIG_SMP
-void synchronize_irq(unsigned int irq)
-{
- /*
- * Not needed, the handler is protected by a lock and IRQs that occur
- * after the handler is deleted are just NOPs.
- */
-}
-EXPORT_SYMBOL_GPL(synchronize_irq);
-#endif
-
-#ifndef CONFIG_PCI
-
-/* Only PCI devices have dynamically-defined IRQ handlers */
-
-int request_irq(unsigned int irq, irq_handler_t handler,
- unsigned long irqflags, const char *devname, void *dev_id)
-{
- return -EINVAL;
-}
-EXPORT_SYMBOL_GPL(request_irq);
-
-void free_irq(unsigned int irq, void *dev_id)
-{
- WARN_ON(1);
-}
-EXPORT_SYMBOL_GPL(free_irq);
-
-void enable_irq(unsigned int irq)
-{
- WARN_ON(1);
-}
-EXPORT_SYMBOL_GPL(enable_irq);
-
-void disable_irq(unsigned int irq)
-{
- WARN_ON(1);
-}
-EXPORT_SYMBOL_GPL(disable_irq);
-
-#endif /* !CONFIG_PCI */
-
-void disable_irq_nosync(unsigned int irq)
-{
- disable_irq(irq);
-}
-EXPORT_SYMBOL_GPL(disable_irq_nosync);
-
-unsigned long probe_irq_on(void)
-{
- return 0;
-}
-EXPORT_SYMBOL_GPL(probe_irq_on);
-
-int probe_irq_off(unsigned long val)
-{
- return 0;
-}
-EXPORT_SYMBOL_GPL(probe_irq_off);
-
-unsigned int probe_irq_mask(unsigned long val)
-{
- return val;
-}
-EXPORT_SYMBOL_GPL(probe_irq_mask);
fixup |= FIXUP_RETURN_REGISTER;
break;
case 0xeb:
- if ((insn[2] & 0xff) == 0x44 || /* bxhg */
- (insn[2] & 0xff) == 0x45) /* bxleg */
+ switch (insn[2] & 0xff) {
+ case 0x44: /* bxhg */
+ case 0x45: /* bxleg */
fixup = FIXUP_BRANCH_NOT_TAKEN;
+ break;
+ }
break;
case 0xe3: /* bctg */
if ((insn[2] & 0xff) == 0x46)
fixup = FIXUP_BRANCH_NOT_TAKEN;
break;
+ case 0xec:
+ switch (insn[2] & 0xff) {
+ case 0xe5: /* clgrb */
+ case 0xe6: /* cgrb */
+ case 0xf6: /* crb */
+ case 0xf7: /* clrb */
+ case 0xfc: /* cgib */
+ case 0xfd: /* cglib */
+ case 0xfe: /* cib */
+ case 0xff: /* clib */
+ fixup = FIXUP_BRANCH_NOT_TAKEN;
+ break;
+ }
+ break;
}
return fixup;
}
: "0", "cc");
#endif
/* Revalidate clock comparator register */
- if (S390_lowcore.clock_comparator == -1)
- set_clock_comparator(S390_lowcore.mcck_clock);
- else
- set_clock_comparator(S390_lowcore.clock_comparator);
+ set_clock_comparator(S390_lowcore.clock_comparator);
/* Check if old PSW is valid */
if (!mci->wp)
/*
}
/* Halt the cpu and keep track of cpu time accounting. */
vtime_stop_cpu();
+ local_irq_enable();
}
void arch_cpu_idle_exit(void)
__ctl_store(cr, 0, 2);
cr_new[1] = cr[1];
- /* Set or clear transaction execution TXC/PIFO bits 8 and 9. */
+ /* Set or clear transaction execution TXC bit 8. */
if (task->thread.per_flags & PER_FLAG_NO_TE)
- cr_new[0] = cr[0] & ~(3UL << 54);
+ cr_new[0] = cr[0] & ~(1UL << 55);
else
- cr_new[0] = cr[0] | (3UL << 54);
+ cr_new[0] = cr[0] | (1UL << 55);
/* Set or clear transaction execution TDC bits 62 and 63. */
cr_new[2] = cr[2] & ~3UL;
if (task->thread.per_flags & PER_FLAG_TE_ABORT_RAND) {
if (!name || *name != 'r')
return -EINVAL;
- if (strict_strtoul(name + 1, 10, &offset))
+ if (kstrtoul(name + 1, 10, &offset))
return -EINVAL;
if (offset >= NUM_GPRS)
return -EINVAL;
#include <linux/suspend.h>
#include <linux/mm.h>
#include <asm/ctl_reg.h>
+#include <asm/ipl.h>
+#include <asm/cio.h>
+#include <asm/pci.h>
/*
* References to section boundaries
__ctl_set_bit(0,28);
local_mcck_enable();
}
+
+/* Called at the end of swsusp_arch_resume */
+void s390_early_resume(void)
+{
+ lgr_info_log();
+ channel_subsystem_reinit();
+ zpci_rescan();
+}
lghi %r2,0
brasl %r14,arch_set_page_states
- /* Log potential guest relocation */
- brasl %r14,lgr_info_log
-
- /* Reinitialize the channel subsystem */
- brasl %r14,channel_subsystem_reinit
+ /* Call arch specific early resume code */
+ brasl %r14,s390_early_resume
/* Return 0 */
lmg %r6,%r15,STACK_FRAME_OVERHEAD + __SF_GPRS(%r15)
struct clock_event_device *cd;
S390_lowcore.clock_comparator = -1ULL;
- set_clock_comparator(S390_lowcore.clock_comparator);
cd = &__get_cpu_var(comparators);
cd->event_handler(cd);
}
else if (strncmp(s, "off", 4) == 0)
vdso_enabled = 0;
else {
- rc = strict_strtoul(s, 0, &val);
+ rc = kstrtoul(s, 0, &val);
vdso_enabled = rc ? 0 : !!val;
}
return !rc;
clear_table((unsigned long *) segment_table, _SEGMENT_ENTRY_EMPTY,
PAGE_SIZE << SEGMENT_ORDER);
- clear_table((unsigned long *) page_table, _PAGE_TYPE_EMPTY,
+ clear_table((unsigned long *) page_table, _PAGE_INVALID,
256*sizeof(unsigned long));
*(unsigned long *) segment_table = _SEGMENT_ENTRY + page_table;
- *(unsigned long *) page_table = _PAGE_RO + page_frame;
+ *(unsigned long *) page_table = _PAGE_PROTECT + page_frame;
psal = (u32 *) (page_table + 256*sizeof(unsigned long));
aste = psal + 32;
do {
set_clock_comparator(end);
vtime_stop_cpu();
- local_irq_disable();
} while (get_tod_clock() < end);
lockdep_on();
__ctl_load(cr0, 0, 0);
set_clock_comparator(end);
}
vtime_stop_cpu();
- local_irq_disable();
if (clock_saved)
local_tick_enable(clock_saved);
} while (get_tod_clock() < end);
switch (mm->context.asce_bits & _ASCE_TYPE_MASK) {
case _ASCE_TYPE_REGION1:
table = table + ((address >> 53) & 0x7ff);
- if (unlikely(*table & _REGION_ENTRY_INV))
+ if (unlikely(*table & _REGION_ENTRY_INVALID))
return -0x39UL;
table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
/* fallthrough */
case _ASCE_TYPE_REGION2:
table = table + ((address >> 42) & 0x7ff);
- if (unlikely(*table & _REGION_ENTRY_INV))
+ if (unlikely(*table & _REGION_ENTRY_INVALID))
return -0x3aUL;
table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
/* fallthrough */
case _ASCE_TYPE_REGION3:
table = table + ((address >> 31) & 0x7ff);
- if (unlikely(*table & _REGION_ENTRY_INV))
+ if (unlikely(*table & _REGION_ENTRY_INVALID))
return -0x3bUL;
table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
/* fallthrough */
case _ASCE_TYPE_SEGMENT:
table = table + ((address >> 20) & 0x7ff);
- if (unlikely(*table & _SEGMENT_ENTRY_INV))
+ if (unlikely(*table & _SEGMENT_ENTRY_INVALID))
return -0x10UL;
if (unlikely(*table & _SEGMENT_ENTRY_LARGE)) {
- if (write && (*table & _SEGMENT_ENTRY_RO))
+ if (write && (*table & _SEGMENT_ENTRY_PROTECT))
return -0x04UL;
return (*table & _SEGMENT_ENTRY_ORIGIN_LARGE) +
(address & ~_SEGMENT_ENTRY_ORIGIN_LARGE);
table = table + ((address >> 12) & 0xff);
if (unlikely(*table & _PAGE_INVALID))
return -0x11UL;
- if (write && (*table & _PAGE_RO))
+ if (write && (*table & _PAGE_PROTECT))
return -0x04UL;
return (*table & PAGE_MASK) + (address & ~PAGE_MASK);
}
unsigned long *table = (unsigned long *)__pa(mm->pgd);
table = table + ((address >> 20) & 0x7ff);
- if (unlikely(*table & _SEGMENT_ENTRY_INV))
+ if (unlikely(*table & _SEGMENT_ENTRY_INVALID))
return -0x10UL;
table = (unsigned long *)(*table & _SEGMENT_ENTRY_ORIGIN);
table = table + ((address >> 12) & 0xff);
if (unlikely(*table & _PAGE_INVALID))
return -0x11UL;
- if (write && (*table & _PAGE_RO))
+ if (write && (*table & _PAGE_PROTECT))
return -0x04UL;
return (*table & PAGE_MASK) + (address & ~PAGE_MASK);
}
seq_printf(m, "I\n");
return;
}
- seq_printf(m, "%s", pr & _PAGE_RO ? "RO " : "RW ");
+ seq_printf(m, "%s", pr & _PAGE_PROTECT ? "RO " : "RW ");
seq_printf(m, "%s", pr & _PAGE_CO ? "CO " : " ");
seq_putc(m, '\n');
}
}
/*
- * The actual page table walker functions. In order to keep the implementation
- * of print_prot() short, we only check and pass _PAGE_INVALID and _PAGE_RO
- * flags to note_page() if a region, segment or page table entry is invalid or
- * read-only.
- * After all it's just a hint that the current level being walked contains an
- * invalid or read-only entry.
+ * The actual page table walker functions. In order to keep the
+ * implementation of print_prot() short, we only check and pass
+ * _PAGE_INVALID and _PAGE_PROTECT flags to note_page() if a region,
+ * segment or page table entry is invalid or read-only.
+ * After all it's just a hint that the current level being walked
+ * contains an invalid or read-only entry.
*/
static void walk_pte_level(struct seq_file *m, struct pg_state *st,
pmd_t *pmd, unsigned long addr)
for (i = 0; i < PTRS_PER_PTE && addr < max_addr; i++) {
st->current_address = addr;
pte = pte_offset_kernel(pmd, addr);
- prot = pte_val(*pte) & (_PAGE_RO | _PAGE_INVALID);
+ prot = pte_val(*pte) & (_PAGE_PROTECT | _PAGE_INVALID);
note_page(m, st, prot, 4);
addr += PAGE_SIZE;
}
}
#ifdef CONFIG_64BIT
-#define _PMD_PROT_MASK (_SEGMENT_ENTRY_RO | _SEGMENT_ENTRY_CO)
+#define _PMD_PROT_MASK (_SEGMENT_ENTRY_PROTECT | _SEGMENT_ENTRY_CO)
#else
#define _PMD_PROT_MASK 0
#endif
pte_t *ptep, pte;
struct page *page;
- mask = (write ? _PAGE_RO : 0) | _PAGE_INVALID | _PAGE_SPECIAL;
+ mask = (write ? _PAGE_PROTECT : 0) | _PAGE_INVALID | _PAGE_SPECIAL;
ptep = ((pte_t *) pmd_deref(pmd)) + pte_index(addr);
do {
struct page *head, *page, *tail;
int refs;
- result = write ? 0 : _SEGMENT_ENTRY_RO;
- mask = result | _SEGMENT_ENTRY_INV;
+ result = write ? 0 : _SEGMENT_ENTRY_PROTECT;
+ mask = result | _SEGMENT_ENTRY_INVALID;
if ((pmd_val(pmd) & mask) != result)
return 0;
VM_BUG_ON(!pfn_valid(pmd_val(pmd) >> PAGE_SHIFT));
#include <linux/mm.h>
#include <linux/hugetlb.h>
+static inline pmd_t __pte_to_pmd(pte_t pte)
+{
+ int none, young, prot;
+ pmd_t pmd;
+
+ /*
+ * Convert encoding pte bits pmd bits
+ * .IR...wrdytp ..R...I...y.
+ * empty .10...000000 -> ..0...1...0.
+ * prot-none, clean, old .11...000001 -> ..0...1...1.
+ * prot-none, clean, young .11...000101 -> ..1...1...1.
+ * prot-none, dirty, old .10...001001 -> ..0...1...1.
+ * prot-none, dirty, young .10...001101 -> ..1...1...1.
+ * read-only, clean, old .11...010001 -> ..1...1...0.
+ * read-only, clean, young .01...010101 -> ..1...0...1.
+ * read-only, dirty, old .11...011001 -> ..1...1...0.
+ * read-only, dirty, young .01...011101 -> ..1...0...1.
+ * read-write, clean, old .11...110001 -> ..0...1...0.
+ * read-write, clean, young .01...110101 -> ..0...0...1.
+ * read-write, dirty, old .10...111001 -> ..0...1...0.
+ * read-write, dirty, young .00...111101 -> ..0...0...1.
+ * Huge ptes are dirty by definition, a clean pte is made dirty
+ * by the conversion.
+ */
+ if (pte_present(pte)) {
+ pmd_val(pmd) = pte_val(pte) & PAGE_MASK;
+ if (pte_val(pte) & _PAGE_INVALID)
+ pmd_val(pmd) |= _SEGMENT_ENTRY_INVALID;
+ none = (pte_val(pte) & _PAGE_PRESENT) &&
+ !(pte_val(pte) & _PAGE_READ) &&
+ !(pte_val(pte) & _PAGE_WRITE);
+ prot = (pte_val(pte) & _PAGE_PROTECT) &&
+ !(pte_val(pte) & _PAGE_WRITE);
+ young = pte_val(pte) & _PAGE_YOUNG;
+ if (none || young)
+ pmd_val(pmd) |= _SEGMENT_ENTRY_YOUNG;
+ if (prot || (none && young))
+ pmd_val(pmd) |= _SEGMENT_ENTRY_PROTECT;
+ } else
+ pmd_val(pmd) = _SEGMENT_ENTRY_INVALID;
+ return pmd;
+}
+
+static inline pte_t __pmd_to_pte(pmd_t pmd)
+{
+ pte_t pte;
+
+ /*
+ * Convert encoding pmd bits pte bits
+ * ..R...I...y. .IR...wrdytp
+ * empty ..0...1...0. -> .10...000000
+ * prot-none, old ..0...1...1. -> .10...001001
+ * prot-none, young ..1...1...1. -> .10...001101
+ * read-only, old ..1...1...0. -> .11...011001
+ * read-only, young ..1...0...1. -> .01...011101
+ * read-write, old ..0...1...0. -> .10...111001
+ * read-write, young ..0...0...1. -> .00...111101
+ * Huge ptes are dirty by definition
+ */
+ if (pmd_present(pmd)) {
+ pte_val(pte) = _PAGE_PRESENT | _PAGE_LARGE | _PAGE_DIRTY |
+ (pmd_val(pmd) & PAGE_MASK);
+ if (pmd_val(pmd) & _SEGMENT_ENTRY_INVALID)
+ pte_val(pte) |= _PAGE_INVALID;
+ if (pmd_prot_none(pmd)) {
+ if (pmd_val(pmd) & _SEGMENT_ENTRY_PROTECT)
+ pte_val(pte) |= _PAGE_YOUNG;
+ } else {
+ pte_val(pte) |= _PAGE_READ;
+ if (pmd_val(pmd) & _SEGMENT_ENTRY_PROTECT)
+ pte_val(pte) |= _PAGE_PROTECT;
+ else
+ pte_val(pte) |= _PAGE_WRITE;
+ if (pmd_val(pmd) & _SEGMENT_ENTRY_YOUNG)
+ pte_val(pte) |= _PAGE_YOUNG;
+ }
+ } else
+ pte_val(pte) = _PAGE_INVALID;
+ return pte;
+}
void set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
- pte_t *pteptr, pte_t pteval)
+ pte_t *ptep, pte_t pte)
{
- pmd_t *pmdp = (pmd_t *) pteptr;
- unsigned long mask;
+ pmd_t pmd;
+ pmd = __pte_to_pmd(pte);
if (!MACHINE_HAS_HPAGE) {
- pteptr = (pte_t *) pte_page(pteval)[1].index;
- mask = pte_val(pteval) &
- (_SEGMENT_ENTRY_INV | _SEGMENT_ENTRY_RO);
- pte_val(pteval) = (_SEGMENT_ENTRY + __pa(pteptr)) | mask;
+ pmd_val(pmd) &= ~_SEGMENT_ENTRY_ORIGIN;
+ pmd_val(pmd) |= pte_page(pte)[1].index;
+ } else
+ pmd_val(pmd) |= _SEGMENT_ENTRY_LARGE | _SEGMENT_ENTRY_CO;
+ *(pmd_t *) ptep = pmd;
+}
+
+pte_t huge_ptep_get(pte_t *ptep)
+{
+ unsigned long origin;
+ pmd_t pmd;
+
+ pmd = *(pmd_t *) ptep;
+ if (!MACHINE_HAS_HPAGE && pmd_present(pmd)) {
+ origin = pmd_val(pmd) & _SEGMENT_ENTRY_ORIGIN;
+ pmd_val(pmd) &= ~_SEGMENT_ENTRY_ORIGIN;
+ pmd_val(pmd) |= *(unsigned long *) origin;
}
+ return __pmd_to_pte(pmd);
+}
- pmd_val(*pmdp) = pte_val(pteval);
+pte_t huge_ptep_get_and_clear(struct mm_struct *mm,
+ unsigned long addr, pte_t *ptep)
+{
+ pmd_t *pmdp = (pmd_t *) ptep;
+ pte_t pte = huge_ptep_get(ptep);
+
+ if (MACHINE_HAS_IDTE)
+ __pmd_idte(addr, pmdp);
+ else
+ __pmd_csp(pmdp);
+ pmd_val(*pmdp) = _SEGMENT_ENTRY_EMPTY;
+ return pte;
}
int arch_prepare_hugepage(struct page *page)
ptep = (pte_t *) page[1].index;
if (!ptep)
return;
- clear_table((unsigned long *) ptep, _PAGE_TYPE_EMPTY,
+ clear_table((unsigned long *) ptep, _PAGE_INVALID,
PTRS_PER_PTE * sizeof(pte_t));
page_table_free(&init_mm, (unsigned long *) ptep);
page[1].index = 0;
pte = pte_offset_kernel(pmd, address);
if (!enable) {
__ptep_ipte(address, pte);
- pte_val(*pte) = _PAGE_TYPE_EMPTY;
+ pte_val(*pte) = _PAGE_INVALID;
continue;
}
pte_val(*pte) = __pa(address);
struct gmap_rmap *rmap;
struct page *page;
- if (*table & _SEGMENT_ENTRY_INV)
+ if (*table & _SEGMENT_ENTRY_INVALID)
return 0;
page = pfn_to_page(*table >> PAGE_SHIFT);
mp = (struct gmap_pgtable *) page->index;
kfree(rmap);
break;
}
- *table = _SEGMENT_ENTRY_INV | _SEGMENT_ENTRY_RO | mp->vmaddr;
+ *table = mp->vmaddr | _SEGMENT_ENTRY_INVALID | _SEGMENT_ENTRY_PROTECT;
return 1;
}
return -ENOMEM;
new = (unsigned long *) page_to_phys(page);
crst_table_init(new, init);
- if (*table & _REGION_ENTRY_INV) {
+ if (*table & _REGION_ENTRY_INVALID) {
list_add(&page->lru, &gmap->crst_list);
*table = (unsigned long) new | _REGION_ENTRY_LENGTH |
(*table & _REGION_ENTRY_TYPE_MASK);
for (off = 0; off < len; off += PMD_SIZE) {
/* Walk the guest addr space page table */
table = gmap->table + (((to + off) >> 53) & 0x7ff);
- if (*table & _REGION_ENTRY_INV)
+ if (*table & _REGION_ENTRY_INVALID)
goto out;
table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
table = table + (((to + off) >> 42) & 0x7ff);
- if (*table & _REGION_ENTRY_INV)
+ if (*table & _REGION_ENTRY_INVALID)
goto out;
table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
table = table + (((to + off) >> 31) & 0x7ff);
- if (*table & _REGION_ENTRY_INV)
+ if (*table & _REGION_ENTRY_INVALID)
goto out;
table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
table = table + (((to + off) >> 20) & 0x7ff);
/* Clear segment table entry in guest address space. */
flush |= gmap_unlink_segment(gmap, table);
- *table = _SEGMENT_ENTRY_INV;
+ *table = _SEGMENT_ENTRY_INVALID;
}
out:
spin_unlock(&gmap->mm->page_table_lock);
for (off = 0; off < len; off += PMD_SIZE) {
/* Walk the gmap address space page table */
table = gmap->table + (((to + off) >> 53) & 0x7ff);
- if ((*table & _REGION_ENTRY_INV) &&
+ if ((*table & _REGION_ENTRY_INVALID) &&
gmap_alloc_table(gmap, table, _REGION2_ENTRY_EMPTY))
goto out_unmap;
table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
table = table + (((to + off) >> 42) & 0x7ff);
- if ((*table & _REGION_ENTRY_INV) &&
+ if ((*table & _REGION_ENTRY_INVALID) &&
gmap_alloc_table(gmap, table, _REGION3_ENTRY_EMPTY))
goto out_unmap;
table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
table = table + (((to + off) >> 31) & 0x7ff);
- if ((*table & _REGION_ENTRY_INV) &&
+ if ((*table & _REGION_ENTRY_INVALID) &&
gmap_alloc_table(gmap, table, _SEGMENT_ENTRY_EMPTY))
goto out_unmap;
table = (unsigned long *) (*table & _REGION_ENTRY_ORIGIN);
/* Store 'from' address in an invalid segment table entry. */
flush |= gmap_unlink_segment(gmap, table);
- *table = _SEGMENT_ENTRY_INV | _SEGMENT_ENTRY_RO | (from + off);
+ *table = (from + off) | (_SEGMENT_ENTRY_INVALID |
+ _SEGMENT_ENTRY_PROTECT);
}
spin_unlock(&gmap->mm->page_table_lock);
up_read(&gmap->mm->mmap_sem);
unsigned long *table;
table = gmap->table + ((address >> 53) & 0x7ff);
- if (unlikely(*table & _REGION_ENTRY_INV))
+ if (unlikely(*table & _REGION_ENTRY_INVALID))
return ERR_PTR(-EFAULT);
table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
table = table + ((address >> 42) & 0x7ff);
- if (unlikely(*table & _REGION_ENTRY_INV))
+ if (unlikely(*table & _REGION_ENTRY_INVALID))
return ERR_PTR(-EFAULT);
table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
table = table + ((address >> 31) & 0x7ff);
- if (unlikely(*table & _REGION_ENTRY_INV))
+ if (unlikely(*table & _REGION_ENTRY_INVALID))
return ERR_PTR(-EFAULT);
table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
table = table + ((address >> 20) & 0x7ff);
return PTR_ERR(segment_ptr);
/* Convert the gmap address to an mm address. */
segment = *segment_ptr;
- if (!(segment & _SEGMENT_ENTRY_INV)) {
+ if (!(segment & _SEGMENT_ENTRY_INVALID)) {
page = pfn_to_page(segment >> PAGE_SHIFT);
mp = (struct gmap_pgtable *) page->index;
return mp->vmaddr | (address & ~PMD_MASK);
- } else if (segment & _SEGMENT_ENTRY_RO) {
+ } else if (segment & _SEGMENT_ENTRY_PROTECT) {
vmaddr = segment & _SEGMENT_ENTRY_ORIGIN;
return vmaddr | (address & ~PMD_MASK);
}
page = pfn_to_page(__pa(table) >> PAGE_SHIFT);
mp = (struct gmap_pgtable *) page->index;
list_for_each_entry_safe(rmap, next, &mp->mapper, list) {
- *rmap->entry =
- _SEGMENT_ENTRY_INV | _SEGMENT_ENTRY_RO | mp->vmaddr;
+ *rmap->entry = mp->vmaddr | (_SEGMENT_ENTRY_INVALID |
+ _SEGMENT_ENTRY_PROTECT);
list_del(&rmap->list);
kfree(rmap);
flush = 1;
/* Convert the gmap address to an mm address. */
while (1) {
segment = *segment_ptr;
- if (!(segment & _SEGMENT_ENTRY_INV)) {
+ if (!(segment & _SEGMENT_ENTRY_INVALID)) {
/* Page table is present */
page = pfn_to_page(segment >> PAGE_SHIFT);
mp = (struct gmap_pgtable *) page->index;
return mp->vmaddr | (address & ~PMD_MASK);
}
- if (!(segment & _SEGMENT_ENTRY_RO))
+ if (!(segment & _SEGMENT_ENTRY_PROTECT))
/* Nothing mapped in the gmap address space. */
break;
rc = gmap_connect_pgtable(address, segment, segment_ptr, gmap);
while (address < to) {
/* Walk the gmap address space page table */
table = gmap->table + ((address >> 53) & 0x7ff);
- if (unlikely(*table & _REGION_ENTRY_INV)) {
+ if (unlikely(*table & _REGION_ENTRY_INVALID)) {
address = (address + PMD_SIZE) & PMD_MASK;
continue;
}
table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
table = table + ((address >> 42) & 0x7ff);
- if (unlikely(*table & _REGION_ENTRY_INV)) {
+ if (unlikely(*table & _REGION_ENTRY_INVALID)) {
address = (address + PMD_SIZE) & PMD_MASK;
continue;
}
table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
table = table + ((address >> 31) & 0x7ff);
- if (unlikely(*table & _REGION_ENTRY_INV)) {
+ if (unlikely(*table & _REGION_ENTRY_INVALID)) {
address = (address + PMD_SIZE) & PMD_MASK;
continue;
}
table = (unsigned long *)(*table & _REGION_ENTRY_ORIGIN);
table = table + ((address >> 20) & 0x7ff);
- if (unlikely(*table & _SEGMENT_ENTRY_INV)) {
+ if (unlikely(*table & _SEGMENT_ENTRY_INVALID)) {
address = (address + PMD_SIZE) & PMD_MASK;
continue;
}
continue;
/* Set notification bit in the pgste of the pte */
entry = *ptep;
- if ((pte_val(entry) & (_PAGE_INVALID | _PAGE_RO)) == 0) {
+ if ((pte_val(entry) & (_PAGE_INVALID | _PAGE_PROTECT)) == 0) {
pgste = pgste_get_lock(ptep);
pgste_val(pgste) |= PGSTE_IN_BIT;
pgste_set_unlock(ptep, pgste);
page->index = (unsigned long) mp;
atomic_set(&page->_mapcount, 3);
table = (unsigned long *) page_to_phys(page);
- clear_table(table, _PAGE_TYPE_EMPTY, PAGE_SIZE/2);
- clear_table(table + PTRS_PER_PTE, 0, PAGE_SIZE/2);
+ clear_table(table, _PAGE_INVALID, PAGE_SIZE/2);
+ clear_table(table + PTRS_PER_PTE, PGSTE_HR_BIT | PGSTE_HC_BIT,
+ PAGE_SIZE/2);
return table;
}
pgste_val(new) |= (key & (_PAGE_CHANGED | _PAGE_REFERENCED)) << 48;
pgste_val(new) |= (key & (_PAGE_ACC_BITS | _PAGE_FP_BIT)) << 56;
if (!(pte_val(*ptep) & _PAGE_INVALID)) {
- unsigned long address, bits;
- unsigned char skey;
+ unsigned long address, bits, skey;
address = pte_val(*ptep) & PAGE_MASK;
- skey = page_get_storage_key(address);
+ skey = (unsigned long) page_get_storage_key(address);
bits = skey & (_PAGE_CHANGED | _PAGE_REFERENCED);
+ skey = key & (_PAGE_ACC_BITS | _PAGE_FP_BIT);
/* Set storage key ACC and FP */
- page_set_storage_key(address,
- (key & (_PAGE_ACC_BITS | _PAGE_FP_BIT)),
- !nq);
-
+ page_set_storage_key(address, skey, !nq);
/* Merge host changed & referenced into pgste */
pgste_val(new) |= bits << 52;
- /* Transfer skey changed & referenced bit to kvm user bits */
- pgste_val(new) |= bits << 45; /* PGSTE_UR_BIT & PGSTE_UC_BIT */
}
/* changing the guest storage key is considered a change of the page */
if ((pgste_val(new) ^ pgste_val(old)) &
(PGSTE_ACC_BITS | PGSTE_FP_BIT | PGSTE_GR_BIT | PGSTE_GC_BIT))
- pgste_val(new) |= PGSTE_UC_BIT;
+ pgste_val(new) |= PGSTE_HC_BIT;
pgste_set_unlock(ptep, new);
pte_unmap_unlock(*ptep, ptl);
pgtable_page_ctor(page);
atomic_set(&page->_mapcount, 1);
table = (unsigned long *) page_to_phys(page);
- clear_table(table, _PAGE_TYPE_EMPTY, PAGE_SIZE);
+ clear_table(table, _PAGE_INVALID, PAGE_SIZE);
spin_lock_bh(&mm->context.list_lock);
list_add(&page->lru, &mm->context.pgtable_list);
} else {
struct mmu_table_batch **batch = &tlb->batch;
if (*batch) {
- __tlb_flush_mm(tlb->mm);
call_rcu_sched(&(*batch)->rcu, tlb_remove_table_rcu);
*batch = NULL;
}
{
struct mmu_table_batch **batch = &tlb->batch;
+ tlb->mm->context.flush_mm = 1;
if (*batch == NULL) {
*batch = (struct mmu_table_batch *)
__get_free_page(GFP_NOWAIT | __GFP_NOWARN);
if (*batch == NULL) {
- __tlb_flush_mm(tlb->mm);
+ __tlb_flush_mm_lazy(tlb->mm);
tlb_remove_table_one(table);
return;
}
}
(*batch)->tables[(*batch)->nr++] = table;
if ((*batch)->nr == MAX_TABLE_BATCH)
- tlb_table_flush(tlb);
+ tlb_flush_mmu(tlb);
}
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
list_del(lh);
}
ptep = (pte_t *) pgtable;
- pte_val(*ptep) = _PAGE_TYPE_EMPTY;
+ pte_val(*ptep) = _PAGE_INVALID;
ptep++;
- pte_val(*ptep) = _PAGE_TYPE_EMPTY;
+ pte_val(*ptep) = _PAGE_INVALID;
return pgtable;
}
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
pte = alloc_bootmem(PTRS_PER_PTE * sizeof(pte_t));
if (!pte)
return NULL;
- clear_table((unsigned long *) pte, _PAGE_TYPE_EMPTY,
+ clear_table((unsigned long *) pte, _PAGE_INVALID,
PTRS_PER_PTE * sizeof(pte_t));
return pte;
}
!(address & ~PUD_MASK) && (address + PUD_SIZE <= end)) {
pud_val(*pu_dir) = __pa(address) |
_REGION_ENTRY_TYPE_R3 | _REGION3_ENTRY_LARGE |
- (ro ? _REGION_ENTRY_RO : 0);
+ (ro ? _REGION_ENTRY_PROTECT : 0);
address += PUD_SIZE;
continue;
}
!(address & ~PMD_MASK) && (address + PMD_SIZE <= end)) {
pmd_val(*pm_dir) = __pa(address) |
_SEGMENT_ENTRY | _SEGMENT_ENTRY_LARGE |
- (ro ? _SEGMENT_ENTRY_RO : 0);
+ _SEGMENT_ENTRY_YOUNG |
+ (ro ? _SEGMENT_ENTRY_PROTECT : 0);
address += PMD_SIZE;
continue;
}
}
pt_dir = pte_offset_kernel(pm_dir, address);
- pte_val(*pt_dir) = __pa(address) | (ro ? _PAGE_RO : 0);
+ pte_val(*pt_dir) = __pa(address) |
+ pgprot_val(ro ? PAGE_KERNEL_RO : PAGE_KERNEL);
address += PAGE_SIZE;
}
ret = 0;
pte_t *pt_dir;
pte_t pte;
- pte_val(pte) = _PAGE_TYPE_EMPTY;
+ pte_val(pte) = _PAGE_INVALID;
while (address < end) {
pg_dir = pgd_offset_k(address);
if (pgd_none(*pg_dir)) {
new_page =__pa(vmem_alloc_pages(0));
if (!new_page)
goto out;
- pte_val(*pt_dir) = __pa(new_page);
+ pte_val(*pt_dir) =
+ __pa(new_page) | pgprot_val(PAGE_KERNEL);
}
address += PAGE_SIZE;
}
# Makefile for the s390 PCI subsystem.
#
-obj-$(CONFIG_PCI) += pci.o pci_dma.o pci_clp.o pci_msi.o pci_sysfs.o \
+obj-$(CONFIG_PCI) += pci.o pci_dma.o pci_clp.o pci_sysfs.o \
pci_event.o pci_debug.o pci_insn.o
#define SIC_IRQ_MODE_SINGLE 1
#define ZPCI_NR_DMA_SPACES 1
-#define ZPCI_MSI_VEC_BITS 6
#define ZPCI_NR_DEVICES CONFIG_PCI_NR_FUNCTIONS
/* list of all detected zpci devices */
-LIST_HEAD(zpci_list);
-EXPORT_SYMBOL_GPL(zpci_list);
-DEFINE_MUTEX(zpci_list_lock);
-EXPORT_SYMBOL_GPL(zpci_list_lock);
+static LIST_HEAD(zpci_list);
+static DEFINE_SPINLOCK(zpci_list_lock);
-static struct pci_hp_callback_ops *hotplug_ops;
+static void zpci_enable_irq(struct irq_data *data);
+static void zpci_disable_irq(struct irq_data *data);
-static DECLARE_BITMAP(zpci_domain, ZPCI_NR_DEVICES);
-static DEFINE_SPINLOCK(zpci_domain_lock);
-
-struct callback {
- irq_handler_t handler;
- void *data;
+static struct irq_chip zpci_irq_chip = {
+ .name = "zPCI",
+ .irq_unmask = zpci_enable_irq,
+ .irq_mask = zpci_disable_irq,
};
-struct zdev_irq_map {
- unsigned long aibv; /* AI bit vector */
- int msi_vecs; /* consecutive MSI-vectors used */
- int __unused;
- struct callback cb[ZPCI_NR_MSI_VECS]; /* callback handler array */
- spinlock_t lock; /* protect callbacks against de-reg */
-};
-
-struct intr_bucket {
- /* amap of adapters, one bit per dev, corresponds to one irq nr */
- unsigned long *alloc;
- /* AI summary bit, global page for all devices */
- unsigned long *aisb;
- /* pointer to aibv and callback data in zdev */
- struct zdev_irq_map *imap[ZPCI_NR_DEVICES];
- /* protects the whole bucket struct */
- spinlock_t lock;
-};
+static DECLARE_BITMAP(zpci_domain, ZPCI_NR_DEVICES);
+static DEFINE_SPINLOCK(zpci_domain_lock);
-static struct intr_bucket *bucket;
+static struct airq_iv *zpci_aisb_iv;
+static struct airq_iv *zpci_aibv[ZPCI_NR_DEVICES];
/* Adapter interrupt definitions */
static void zpci_irq_handler(struct airq_struct *airq);
struct zpci_iomap_entry *zpci_iomap_start;
EXPORT_SYMBOL_GPL(zpci_iomap_start);
-/* highest irq summary bit */
-static int __read_mostly aisb_max;
-
-static struct kmem_cache *zdev_irq_cache;
static struct kmem_cache *zdev_fmb_cache;
-static inline int irq_to_msi_nr(unsigned int irq)
-{
- return irq & ZPCI_MSI_MASK;
-}
-
-static inline int irq_to_dev_nr(unsigned int irq)
-{
- return irq >> ZPCI_MSI_VEC_BITS;
-}
-
-static inline struct zdev_irq_map *get_imap(unsigned int irq)
-{
- return bucket->imap[irq_to_dev_nr(irq)];
-}
-
struct zpci_dev *get_zdev(struct pci_dev *pdev)
{
return (struct zpci_dev *) pdev->sysdata;
{
struct zpci_dev *tmp, *zdev = NULL;
- mutex_lock(&zpci_list_lock);
+ spin_lock(&zpci_list_lock);
list_for_each_entry(tmp, &zpci_list, entry) {
if (tmp->fid == fid) {
zdev = tmp;
break;
}
}
- mutex_unlock(&zpci_list_lock);
+ spin_unlock(&zpci_list_lock);
return zdev;
}
-bool zpci_fid_present(u32 fid)
-{
- return (get_zdev_by_fid(fid) != NULL) ? true : false;
-}
-
static struct zpci_dev *get_zdev_by_bus(struct pci_bus *bus)
{
return (bus && bus->sysdata) ? (struct zpci_dev *) bus->sysdata : NULL;
EXPORT_SYMBOL_GPL(pci_proc_domain);
/* Modify PCI: Register adapter interruptions */
-static int zpci_register_airq(struct zpci_dev *zdev, unsigned int aisb,
- u64 aibv)
+static int zpci_set_airq(struct zpci_dev *zdev)
{
u64 req = ZPCI_CREATE_REQ(zdev->fh, 0, ZPCI_MOD_FC_REG_INT);
struct zpci_fib *fib;
return -ENOMEM;
fib->isc = PCI_ISC;
- fib->noi = zdev->irq_map->msi_vecs;
fib->sum = 1; /* enable summary notifications */
- fib->aibv = aibv;
- fib->aibvo = 0; /* every function has its own page */
- fib->aisb = (u64) bucket->aisb + aisb / 8;
- fib->aisbo = aisb & ZPCI_MSI_MASK;
+ fib->noi = airq_iv_end(zdev->aibv);
+ fib->aibv = (unsigned long) zdev->aibv->vector;
+ fib->aibvo = 0; /* each zdev has its own interrupt vector */
+ fib->aisb = (unsigned long) zpci_aisb_iv->vector + (zdev->aisb/64)*8;
+ fib->aisbo = zdev->aisb & 63;
- rc = s390pci_mod_fc(req, fib);
+ rc = zpci_mod_fc(req, fib);
pr_debug("%s mpcifc returned noi: %d\n", __func__, fib->noi);
free_page((unsigned long) fib);
fib->iota = args->iota;
fib->fmb_addr = args->fmb_addr;
- rc = s390pci_mod_fc(req, fib);
+ rc = zpci_mod_fc(req, fib);
free_page((unsigned long) fib);
return rc;
}
}
/* Modify PCI: Unregister adapter interruptions */
-static int zpci_unregister_airq(struct zpci_dev *zdev)
+static int zpci_clear_airq(struct zpci_dev *zdev)
{
struct mod_pci_args args = { 0, 0, 0, 0 };
u64 data;
int rc;
- rc = s390pci_load(&data, req, offset);
+ rc = zpci_load(&data, req, offset);
if (!rc) {
data = data << ((8 - len) * 8);
data = le64_to_cpu(data);
data = cpu_to_le64(data);
data = data >> ((8 - len) * 8);
- rc = s390pci_store(data, req, offset);
+ rc = zpci_store(data, req, offset);
return rc;
}
-void enable_irq(unsigned int irq)
+static int zpci_msi_set_mask_bits(struct msi_desc *msi, u32 mask, u32 flag)
+{
+ int offset, pos;
+ u32 mask_bits;
+
+ if (msi->msi_attrib.is_msix) {
+ offset = msi->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE +
+ PCI_MSIX_ENTRY_VECTOR_CTRL;
+ msi->masked = readl(msi->mask_base + offset);
+ writel(flag, msi->mask_base + offset);
+ } else if (msi->msi_attrib.maskbit) {
+ pos = (long) msi->mask_base;
+ pci_read_config_dword(msi->dev, pos, &mask_bits);
+ mask_bits &= ~(mask);
+ mask_bits |= flag & mask;
+ pci_write_config_dword(msi->dev, pos, mask_bits);
+ } else
+ return 0;
+
+ msi->msi_attrib.maskbit = !!flag;
+ return 1;
+}
+
+static void zpci_enable_irq(struct irq_data *data)
{
- struct msi_desc *msi = irq_get_msi_desc(irq);
+ struct msi_desc *msi = irq_get_msi_desc(data->irq);
zpci_msi_set_mask_bits(msi, 1, 0);
}
-EXPORT_SYMBOL_GPL(enable_irq);
-void disable_irq(unsigned int irq)
+static void zpci_disable_irq(struct irq_data *data)
{
- struct msi_desc *msi = irq_get_msi_desc(irq);
+ struct msi_desc *msi = irq_get_msi_desc(data->irq);
zpci_msi_set_mask_bits(msi, 1, 1);
}
-EXPORT_SYMBOL_GPL(disable_irq);
void pcibios_fixup_bus(struct pci_bus *bus)
{
.write = pci_write,
};
-/* store the last handled bit to implement fair scheduling of devices */
-static DEFINE_PER_CPU(unsigned long, next_sbit);
-
static void zpci_irq_handler(struct airq_struct *airq)
{
- unsigned long sbit, mbit, last = 0, start = __get_cpu_var(next_sbit);
- int rescan = 0, max = aisb_max;
- struct zdev_irq_map *imap;
+ unsigned long si, ai;
+ struct airq_iv *aibv;
+ int irqs_on = 0;
inc_irq_stat(IRQIO_PCI);
- sbit = start;
-
-scan:
- /* find summary_bit */
- for_each_set_bit_left_cont(sbit, bucket->aisb, max) {
- clear_bit(63 - (sbit & 63), bucket->aisb + (sbit >> 6));
- last = sbit;
+ for (si = 0;;) {
+ /* Scan adapter summary indicator bit vector */
+ si = airq_iv_scan(zpci_aisb_iv, si, airq_iv_end(zpci_aisb_iv));
+ if (si == -1UL) {
+ if (irqs_on++)
+ /* End of second scan with interrupts on. */
+ break;
+ /* First scan complete, reenable interrupts. */
+ zpci_set_irq_ctrl(SIC_IRQ_MODE_SINGLE, NULL, PCI_ISC);
+ si = 0;
+ continue;
+ }
- /* find vector bit */
- imap = bucket->imap[sbit];
- for_each_set_bit_left(mbit, &imap->aibv, imap->msi_vecs) {
+ /* Scan the adapter interrupt vector for this device. */
+ aibv = zpci_aibv[si];
+ for (ai = 0;;) {
+ ai = airq_iv_scan(aibv, ai, airq_iv_end(aibv));
+ if (ai == -1UL)
+ break;
inc_irq_stat(IRQIO_MSI);
- clear_bit(63 - mbit, &imap->aibv);
-
- spin_lock(&imap->lock);
- if (imap->cb[mbit].handler)
- imap->cb[mbit].handler(mbit,
- imap->cb[mbit].data);
- spin_unlock(&imap->lock);
+ airq_iv_lock(aibv, ai);
+ generic_handle_irq(airq_iv_get_data(aibv, ai));
+ airq_iv_unlock(aibv, ai);
}
}
-
- if (rescan)
- goto out;
-
- /* scan the skipped bits */
- if (start > 0) {
- sbit = 0;
- max = start;
- start = 0;
- goto scan;
- }
-
- /* enable interrupts again */
- set_irq_ctrl(SIC_IRQ_MODE_SINGLE, NULL, PCI_ISC);
-
- /* check again to not lose initiative */
- rmb();
- max = aisb_max;
- sbit = find_first_bit_left(bucket->aisb, max);
- if (sbit != max) {
- rescan++;
- goto scan;
- }
-out:
- /* store next device bit to scan */
- __get_cpu_var(next_sbit) = (++last >= aisb_max) ? 0 : last;
}
-/* msi_vecs - number of requested interrupts, 0 place function to error state */
-static int zpci_setup_msi(struct pci_dev *pdev, int msi_vecs)
+int arch_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
{
struct zpci_dev *zdev = get_zdev(pdev);
- unsigned int aisb, msi_nr;
+ unsigned int hwirq, irq, msi_vecs;
+ unsigned long aisb;
struct msi_desc *msi;
+ struct msi_msg msg;
int rc;
- /* store the number of used MSI vectors */
- zdev->irq_map->msi_vecs = min(msi_vecs, ZPCI_NR_MSI_VECS);
-
- spin_lock(&bucket->lock);
- aisb = find_first_zero_bit(bucket->alloc, PAGE_SIZE);
- /* alloc map exhausted? */
- if (aisb == PAGE_SIZE) {
- spin_unlock(&bucket->lock);
- return -EIO;
- }
- set_bit(aisb, bucket->alloc);
- spin_unlock(&bucket->lock);
+ pr_debug("%s: requesting %d MSI-X interrupts...", __func__, nvec);
+ if (type != PCI_CAP_ID_MSIX && type != PCI_CAP_ID_MSI)
+ return -EINVAL;
+ msi_vecs = min(nvec, ZPCI_MSI_VEC_MAX);
+ msi_vecs = min_t(unsigned int, msi_vecs, CONFIG_PCI_NR_MSI);
+ /* Allocate adapter summary indicator bit */
+ rc = -EIO;
+ aisb = airq_iv_alloc_bit(zpci_aisb_iv);
+ if (aisb == -1UL)
+ goto out;
zdev->aisb = aisb;
- if (aisb + 1 > aisb_max)
- aisb_max = aisb + 1;
- /* wire up IRQ shortcut pointer */
- bucket->imap[zdev->aisb] = zdev->irq_map;
- pr_debug("%s: imap[%u] linked to %p\n", __func__, zdev->aisb, zdev->irq_map);
+ /* Create adapter interrupt vector */
+ rc = -ENOMEM;
+ zdev->aibv = airq_iv_create(msi_vecs, AIRQ_IV_DATA | AIRQ_IV_BITLOCK);
+ if (!zdev->aibv)
+ goto out_si;
- /* TODO: irq number 0 wont be found if we return less than requested MSIs.
- * ignore it for now and fix in common code.
- */
- msi_nr = aisb << ZPCI_MSI_VEC_BITS;
+ /* Wire up shortcut pointer */
+ zpci_aibv[aisb] = zdev->aibv;
+ /* Request MSI interrupts */
+ hwirq = 0;
list_for_each_entry(msi, &pdev->msi_list, list) {
- rc = zpci_setup_msi_irq(zdev, msi, msi_nr,
- aisb << ZPCI_MSI_VEC_BITS);
+ rc = -EIO;
+ irq = irq_alloc_desc(0); /* Alloc irq on node 0 */
+ if (irq == NO_IRQ)
+ goto out_msi;
+ rc = irq_set_msi_desc(irq, msi);
if (rc)
- return rc;
- msi_nr++;
+ goto out_msi;
+ irq_set_chip_and_handler(irq, &zpci_irq_chip,
+ handle_simple_irq);
+ msg.data = hwirq;
+ msg.address_lo = zdev->msi_addr & 0xffffffff;
+ msg.address_hi = zdev->msi_addr >> 32;
+ write_msi_msg(irq, &msg);
+ airq_iv_set_data(zdev->aibv, hwirq, irq);
+ hwirq++;
}
- rc = zpci_register_airq(zdev, aisb, (u64) &zdev->irq_map->aibv);
- if (rc) {
- clear_bit(aisb, bucket->alloc);
- dev_err(&pdev->dev, "register MSI failed with: %d\n", rc);
- return rc;
+ /* Enable adapter interrupts */
+ rc = zpci_set_airq(zdev);
+ if (rc)
+ goto out_msi;
+
+ return (msi_vecs == nvec) ? 0 : msi_vecs;
+
+out_msi:
+ list_for_each_entry(msi, &pdev->msi_list, list) {
+ if (hwirq-- == 0)
+ break;
+ irq_set_msi_desc(msi->irq, NULL);
+ irq_free_desc(msi->irq);
+ msi->msg.address_lo = 0;
+ msi->msg.address_hi = 0;
+ msi->msg.data = 0;
+ msi->irq = 0;
}
- return (zdev->irq_map->msi_vecs == msi_vecs) ?
- 0 : zdev->irq_map->msi_vecs;
+ zpci_aibv[aisb] = NULL;
+ airq_iv_release(zdev->aibv);
+out_si:
+ airq_iv_free_bit(zpci_aisb_iv, aisb);
+out:
+ dev_err(&pdev->dev, "register MSI failed with: %d\n", rc);
+ return rc;
}
-static void zpci_teardown_msi(struct pci_dev *pdev)
+void arch_teardown_msi_irqs(struct pci_dev *pdev)
{
struct zpci_dev *zdev = get_zdev(pdev);
struct msi_desc *msi;
- int aisb, rc;
+ int rc;
- rc = zpci_unregister_airq(zdev);
+ pr_info("%s: on pdev: %p\n", __func__, pdev);
+
+ /* Disable adapter interrupts */
+ rc = zpci_clear_airq(zdev);
if (rc) {
dev_err(&pdev->dev, "deregister MSI failed with: %d\n", rc);
return;
}
- msi = list_first_entry(&pdev->msi_list, struct msi_desc, list);
- aisb = irq_to_dev_nr(msi->irq);
-
- list_for_each_entry(msi, &pdev->msi_list, list)
- zpci_teardown_msi_irq(zdev, msi);
-
- clear_bit(aisb, bucket->alloc);
- if (aisb + 1 == aisb_max)
- aisb_max--;
-}
-
-int arch_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
-{
- pr_debug("%s: requesting %d MSI-X interrupts...", __func__, nvec);
- if (type != PCI_CAP_ID_MSIX && type != PCI_CAP_ID_MSI)
- return -EINVAL;
- return zpci_setup_msi(pdev, nvec);
-}
+ /* Release MSI interrupts */
+ list_for_each_entry(msi, &pdev->msi_list, list) {
+ zpci_msi_set_mask_bits(msi, 1, 1);
+ irq_set_msi_desc(msi->irq, NULL);
+ irq_free_desc(msi->irq);
+ msi->msg.address_lo = 0;
+ msi->msg.address_hi = 0;
+ msi->msg.data = 0;
+ msi->irq = 0;
+ }
-void arch_teardown_msi_irqs(struct pci_dev *pdev)
-{
- pr_info("%s: on pdev: %p\n", __func__, pdev);
- zpci_teardown_msi(pdev);
+ zpci_aibv[zdev->aisb] = NULL;
+ airq_iv_release(zdev->aibv);
+ airq_iv_free_bit(zpci_aisb_iv, zdev->aisb);
}
static void zpci_map_resources(struct zpci_dev *zdev)
continue;
pdev->resource[i].start = (resource_size_t) pci_iomap(pdev, i, 0);
pdev->resource[i].end = pdev->resource[i].start + len - 1;
- pr_debug("BAR%i: -> start: %Lx end: %Lx\n",
- i, pdev->resource[i].start, pdev->resource[i].end);
}
}
/* Alloc memory for our private pci device data */
zdev = kzalloc(sizeof(*zdev), GFP_KERNEL);
- if (!zdev)
- return ERR_PTR(-ENOMEM);
-
- /* Alloc aibv & callback space */
- zdev->irq_map = kmem_cache_zalloc(zdev_irq_cache, GFP_KERNEL);
- if (!zdev->irq_map)
- goto error;
- WARN_ON((u64) zdev->irq_map & 0xff);
- return zdev;
-
-error:
- kfree(zdev);
- return ERR_PTR(-ENOMEM);
+ return zdev ? : ERR_PTR(-ENOMEM);
}
void zpci_free_device(struct zpci_dev *zdev)
{
- kmem_cache_free(zdev_irq_cache, zdev->irq_map);
kfree(zdev);
}
-/*
- * Too late for any s390 specific setup, since interrupts must be set up
- * already which requires DMA setup too and the pci scan will access the
- * config space, which only works if the function handle is enabled.
- */
-int pcibios_enable_device(struct pci_dev *pdev, int mask)
-{
- struct resource *res;
- u16 cmd;
- int i;
-
- pci_read_config_word(pdev, PCI_COMMAND, &cmd);
-
- for (i = 0; i < PCI_BAR_COUNT; i++) {
- res = &pdev->resource[i];
-
- if (res->flags & IORESOURCE_IO)
- return -EINVAL;
-
- if (res->flags & IORESOURCE_MEM)
- cmd |= PCI_COMMAND_MEMORY;
- }
- pci_write_config_word(pdev, PCI_COMMAND, cmd);
- return 0;
-}
-
int pcibios_add_platform_entries(struct pci_dev *pdev)
{
return zpci_sysfs_add_device(&pdev->dev);
}
-int zpci_request_irq(unsigned int irq, irq_handler_t handler, void *data)
-{
- int msi_nr = irq_to_msi_nr(irq);
- struct zdev_irq_map *imap;
- struct msi_desc *msi;
-
- msi = irq_get_msi_desc(irq);
- if (!msi)
- return -EIO;
-
- imap = get_imap(irq);
- spin_lock_init(&imap->lock);
-
- pr_debug("%s: register handler for IRQ:MSI %d:%d\n", __func__, irq >> 6, msi_nr);
- imap->cb[msi_nr].handler = handler;
- imap->cb[msi_nr].data = data;
-
- /*
- * The generic MSI code returns with the interrupt disabled on the
- * card, using the MSI mask bits. Firmware doesn't appear to unmask
- * at that level, so we do it here by hand.
- */
- zpci_msi_set_mask_bits(msi, 1, 0);
- return 0;
-}
-
-void zpci_free_irq(unsigned int irq)
-{
- struct zdev_irq_map *imap = get_imap(irq);
- int msi_nr = irq_to_msi_nr(irq);
- unsigned long flags;
-
- pr_debug("%s: for irq: %d\n", __func__, irq);
-
- spin_lock_irqsave(&imap->lock, flags);
- imap->cb[msi_nr].handler = NULL;
- imap->cb[msi_nr].data = NULL;
- spin_unlock_irqrestore(&imap->lock, flags);
-}
-
-int request_irq(unsigned int irq, irq_handler_t handler,
- unsigned long irqflags, const char *devname, void *dev_id)
-{
- pr_debug("%s: irq: %d handler: %p flags: %lx dev: %s\n",
- __func__, irq, handler, irqflags, devname);
-
- return zpci_request_irq(irq, handler, dev_id);
-}
-EXPORT_SYMBOL_GPL(request_irq);
-
-void free_irq(unsigned int irq, void *dev_id)
-{
- zpci_free_irq(irq);
-}
-EXPORT_SYMBOL_GPL(free_irq);
-
static int __init zpci_irq_init(void)
{
- int cpu, rc;
-
- bucket = kzalloc(sizeof(*bucket), GFP_KERNEL);
- if (!bucket)
- return -ENOMEM;
-
- bucket->aisb = (unsigned long *) get_zeroed_page(GFP_KERNEL);
- if (!bucket->aisb) {
- rc = -ENOMEM;
- goto out_aisb;
- }
-
- bucket->alloc = (unsigned long *) get_zeroed_page(GFP_KERNEL);
- if (!bucket->alloc) {
- rc = -ENOMEM;
- goto out_alloc;
- }
+ int rc;
rc = register_adapter_interrupt(&zpci_airq);
if (rc)
- goto out_ai;
+ goto out;
/* Set summary to 1 to be called every time for the ISC. */
*zpci_airq.lsi_ptr = 1;
- for_each_online_cpu(cpu)
- per_cpu(next_sbit, cpu) = 0;
+ rc = -ENOMEM;
+ zpci_aisb_iv = airq_iv_create(ZPCI_NR_DEVICES, AIRQ_IV_ALLOC);
+ if (!zpci_aisb_iv)
+ goto out_airq;
- spin_lock_init(&bucket->lock);
- set_irq_ctrl(SIC_IRQ_MODE_SINGLE, NULL, PCI_ISC);
+ zpci_set_irq_ctrl(SIC_IRQ_MODE_SINGLE, NULL, PCI_ISC);
return 0;
-out_ai:
- free_page((unsigned long) bucket->alloc);
-out_alloc:
- free_page((unsigned long) bucket->aisb);
-out_aisb:
- kfree(bucket);
+out_airq:
+ unregister_adapter_interrupt(&zpci_airq);
+out:
return rc;
}
static void zpci_irq_exit(void)
{
- free_page((unsigned long) bucket->alloc);
- free_page((unsigned long) bucket->aisb);
+ airq_iv_release(zpci_aisb_iv);
unregister_adapter_interrupt(&zpci_airq);
- kfree(bucket);
}
static struct resource *zpci_alloc_bus_resource(unsigned long start, unsigned long size,
int pcibios_add_device(struct pci_dev *pdev)
{
struct zpci_dev *zdev = get_zdev(pdev);
+ struct resource *res;
+ int i;
+
+ zdev->pdev = pdev;
+ zpci_map_resources(zdev);
+
+ for (i = 0; i < PCI_BAR_COUNT; i++) {
+ res = &pdev->resource[i];
+ if (res->parent || !res->flags)
+ continue;
+ pci_claim_resource(pdev, i);
+ }
+
+ return 0;
+}
+
+int pcibios_enable_device(struct pci_dev *pdev, int mask)
+{
+ struct zpci_dev *zdev = get_zdev(pdev);
+ struct resource *res;
+ u16 cmd;
+ int i;
zdev->pdev = pdev;
zpci_debug_init_device(zdev);
zpci_fmb_enable_device(zdev);
zpci_map_resources(zdev);
+ pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+ for (i = 0; i < PCI_BAR_COUNT; i++) {
+ res = &pdev->resource[i];
+
+ if (res->flags & IORESOURCE_IO)
+ return -EINVAL;
+
+ if (res->flags & IORESOURCE_MEM)
+ cmd |= PCI_COMMAND_MEMORY;
+ }
+ pci_write_config_word(pdev, PCI_COMMAND, cmd);
return 0;
}
-void pcibios_release_device(struct pci_dev *pdev)
+void pcibios_disable_device(struct pci_dev *pdev)
{
struct zpci_dev *zdev = get_zdev(pdev);
rc = zpci_dma_init_device(zdev);
if (rc)
goto out_dma;
+
+ zdev->state = ZPCI_FN_STATE_ONLINE;
return 0;
out_dma:
rc = zpci_enable_device(zdev);
if (rc)
goto out_free;
-
- zdev->state = ZPCI_FN_STATE_ONLINE;
}
rc = zpci_scan_bus(zdev);
if (rc)
goto out_disable;
- mutex_lock(&zpci_list_lock);
+ spin_lock(&zpci_list_lock);
list_add_tail(&zdev->entry, &zpci_list);
- if (hotplug_ops)
- hotplug_ops->create_slot(zdev);
- mutex_unlock(&zpci_list_lock);
+ spin_unlock(&zpci_list_lock);
+
+ zpci_init_slot(zdev);
return 0;
static int zpci_mem_init(void)
{
- zdev_irq_cache = kmem_cache_create("PCI_IRQ_cache", sizeof(struct zdev_irq_map),
- L1_CACHE_BYTES, SLAB_HWCACHE_ALIGN, NULL);
- if (!zdev_irq_cache)
- goto error_zdev;
-
zdev_fmb_cache = kmem_cache_create("PCI_FMB_cache", sizeof(struct zpci_fmb),
16, 0, NULL);
if (!zdev_fmb_cache)
- goto error_fmb;
+ goto error_zdev;
/* TODO: use realloc */
zpci_iomap_start = kzalloc(ZPCI_IOMAP_MAX_ENTRIES * sizeof(*zpci_iomap_start),
error_iomap:
kmem_cache_destroy(zdev_fmb_cache);
-error_fmb:
- kmem_cache_destroy(zdev_irq_cache);
error_zdev:
return -ENOMEM;
}
static void zpci_mem_exit(void)
{
kfree(zpci_iomap_start);
- kmem_cache_destroy(zdev_irq_cache);
kmem_cache_destroy(zdev_fmb_cache);
}
-void zpci_register_hp_ops(struct pci_hp_callback_ops *ops)
-{
- mutex_lock(&zpci_list_lock);
- hotplug_ops = ops;
- mutex_unlock(&zpci_list_lock);
-}
-EXPORT_SYMBOL_GPL(zpci_register_hp_ops);
-
-void zpci_deregister_hp_ops(void)
-{
- mutex_lock(&zpci_list_lock);
- hotplug_ops = NULL;
- mutex_unlock(&zpci_list_lock);
-}
-EXPORT_SYMBOL_GPL(zpci_deregister_hp_ops);
-
-unsigned int s390_pci_probe;
-EXPORT_SYMBOL_GPL(s390_pci_probe);
+static unsigned int s390_pci_probe;
char * __init pcibios_setup(char *str)
{
rc = zpci_debug_init();
if (rc)
- return rc;
+ goto out;
rc = zpci_mem_init();
if (rc)
goto out_mem;
- rc = zpci_msihash_init();
- if (rc)
- goto out_hash;
-
rc = zpci_irq_init();
if (rc)
goto out_irq;
if (rc)
goto out_dma;
- rc = clp_find_pci_devices();
+ rc = clp_scan_pci_devices();
if (rc)
goto out_find;
out_dma:
zpci_irq_exit();
out_irq:
- zpci_msihash_exit();
-out_hash:
zpci_mem_exit();
out_mem:
zpci_debug_exit();
+out:
return rc;
}
-subsys_initcall(pci_base_init);
+subsys_initcall_sync(pci_base_init);
+
+void zpci_rescan(void)
+{
+ clp_rescan_pci_devices_simple();
+}
return cc;
}
-static void *clp_alloc_block(void)
+static void *clp_alloc_block(gfp_t gfp_mask)
{
- return (void *) __get_free_pages(GFP_KERNEL, get_order(CLP_BLK_SIZE));
+ return (void *) __get_free_pages(gfp_mask, get_order(CLP_BLK_SIZE));
}
static void clp_free_block(void *ptr)
struct clp_req_rsp_query_pci_grp *rrb;
int rc;
- rrb = clp_alloc_block();
+ rrb = clp_alloc_block(GFP_KERNEL);
if (!rrb)
return -ENOMEM;
struct clp_req_rsp_query_pci *rrb;
int rc;
- rrb = clp_alloc_block();
+ rrb = clp_alloc_block(GFP_KERNEL);
if (!rrb)
return -ENOMEM;
static int clp_set_pci_fn(u32 *fh, u8 nr_dma_as, u8 command)
{
struct clp_req_rsp_set_pci *rrb;
- int rc, retries = 1000;
+ int rc, retries = 100;
- rrb = clp_alloc_block();
+ rrb = clp_alloc_block(GFP_KERNEL);
if (!rrb)
return -ENOMEM;
retries--;
if (retries < 0)
break;
- msleep(1);
+ msleep(20);
}
} while (rrb->response.hdr.rsp == CLP_RC_SETPCIFN_BUSY);
return rc;
}
-static void clp_check_pcifn_entry(struct clp_fh_list_entry *entry)
+static int clp_list_pci(struct clp_req_rsp_list_pci *rrb,
+ void (*cb)(struct clp_fh_list_entry *entry))
{
- int present, rc;
-
- if (!entry->vendor_id)
- return;
-
- /* TODO: be a little bit more scalable */
- present = zpci_fid_present(entry->fid);
-
- if (present)
- pr_debug("%s: device %x already present\n", __func__, entry->fid);
-
- /* skip already used functions */
- if (present && entry->config_state)
- return;
-
- /* aev 306: function moved to stand-by state */
- if (present && !entry->config_state) {
- /*
- * The handle is already disabled, that means no iota/irq freeing via
- * the firmware interfaces anymore. Need to free resources manually
- * (DMA memory, debug, sysfs)...
- */
- zpci_stop_device(get_zdev_by_fid(entry->fid));
- return;
- }
-
- rc = clp_add_pci_device(entry->fid, entry->fh, entry->config_state);
- if (rc)
- pr_err("Failed to add fid: 0x%x\n", entry->fid);
-}
-
-int clp_find_pci_devices(void)
-{
- struct clp_req_rsp_list_pci *rrb;
u64 resume_token = 0;
int entries, i, rc;
- rrb = clp_alloc_block();
- if (!rrb)
- return -ENOMEM;
-
do {
memset(rrb, 0, sizeof(*rrb));
rrb->request.hdr.len = sizeof(rrb->request);
resume_token = rrb->response.resume_token;
for (i = 0; i < entries; i++)
- clp_check_pcifn_entry(&rrb->response.fh_list[i]);
+ cb(&rrb->response.fh_list[i]);
} while (resume_token);
pr_debug("Maximum number of supported PCI functions: %u\n",
rrb->response.max_fn);
out:
+ return rc;
+}
+
+static void __clp_add(struct clp_fh_list_entry *entry)
+{
+ if (!entry->vendor_id)
+ return;
+
+ clp_add_pci_device(entry->fid, entry->fh, entry->config_state);
+}
+
+static void __clp_rescan(struct clp_fh_list_entry *entry)
+{
+ struct zpci_dev *zdev;
+
+ if (!entry->vendor_id)
+ return;
+
+ zdev = get_zdev_by_fid(entry->fid);
+ if (!zdev) {
+ clp_add_pci_device(entry->fid, entry->fh, entry->config_state);
+ return;
+ }
+
+ if (!entry->config_state) {
+ /*
+ * The handle is already disabled, that means no iota/irq freeing via
+ * the firmware interfaces anymore. Need to free resources manually
+ * (DMA memory, debug, sysfs)...
+ */
+ zpci_stop_device(zdev);
+ }
+}
+
+static void __clp_update(struct clp_fh_list_entry *entry)
+{
+ struct zpci_dev *zdev;
+
+ if (!entry->vendor_id)
+ return;
+
+ zdev = get_zdev_by_fid(entry->fid);
+ if (!zdev)
+ return;
+
+ zdev->fh = entry->fh;
+}
+
+int clp_scan_pci_devices(void)
+{
+ struct clp_req_rsp_list_pci *rrb;
+ int rc;
+
+ rrb = clp_alloc_block(GFP_KERNEL);
+ if (!rrb)
+ return -ENOMEM;
+
+ rc = clp_list_pci(rrb, __clp_add);
+
+ clp_free_block(rrb);
+ return rc;
+}
+
+int clp_rescan_pci_devices(void)
+{
+ struct clp_req_rsp_list_pci *rrb;
+ int rc;
+
+ rrb = clp_alloc_block(GFP_KERNEL);
+ if (!rrb)
+ return -ENOMEM;
+
+ rc = clp_list_pci(rrb, __clp_rescan);
+
+ clp_free_block(rrb);
+ return rc;
+}
+
+int clp_rescan_pci_devices_simple(void)
+{
+ struct clp_req_rsp_list_pci *rrb;
+ int rc;
+
+ rrb = clp_alloc_block(GFP_NOWAIT);
+ if (!rrb)
+ return -ENOMEM;
+
+ rc = clp_list_pci(rrb, __clp_update);
+
clp_free_block(rrb);
return rc;
}
#include <linux/export.h>
#include <linux/iommu-helper.h>
#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
#include <linux/pci.h>
#include <asm/pci_dma.h>
*/
goto no_refresh;
- rc = s390pci_refresh_trans((u64) zdev->fh << 32, start_dma_addr,
- nr_pages * PAGE_SIZE);
+ rc = zpci_refresh_trans((u64) zdev->fh << 32, start_dma_addr,
+ nr_pages * PAGE_SIZE);
no_refresh:
spin_unlock_irqrestore(&zdev->dma_table_lock, irq_flags);
int zpci_dma_init_device(struct zpci_dev *zdev)
{
- unsigned int bitmap_order;
int rc;
spin_lock_init(&zdev->iommu_bitmap_lock);
zdev->iommu_size = (unsigned long) high_memory - PAGE_OFFSET;
zdev->iommu_pages = zdev->iommu_size >> PAGE_SHIFT;
- bitmap_order = get_order(zdev->iommu_pages / 8);
- pr_info("iommu_size: 0x%lx iommu_pages: 0x%lx bitmap_order: %i\n",
- zdev->iommu_size, zdev->iommu_pages, bitmap_order);
-
- zdev->iommu_bitmap = (void *) __get_free_pages(GFP_KERNEL | __GFP_ZERO,
- bitmap_order);
+ zdev->iommu_bitmap = vzalloc(zdev->iommu_pages / 8);
if (!zdev->iommu_bitmap) {
rc = -ENOMEM;
goto out_reg;
{
zpci_unregister_ioat(zdev, 0);
dma_cleanup_tables(zdev);
- free_pages((unsigned long) zdev->iommu_bitmap,
- get_order(zdev->iommu_pages / 8));
+ vfree(zdev->iommu_bitmap);
zdev->iommu_bitmap = NULL;
zdev->next_bit = 0;
}
clp_add_pci_device(ccdf->fid, ccdf->fh, 0);
break;
case 0x0306:
- clp_find_pci_devices();
+ clp_rescan_pci_devices();
break;
default:
break;
return cc;
}
-int s390pci_mod_fc(u64 req, struct zpci_fib *fib)
+int zpci_mod_fc(u64 req, struct zpci_fib *fib)
{
u8 cc, status;
return cc;
}
-int s390pci_refresh_trans(u64 fn, u64 addr, u64 range)
+int zpci_refresh_trans(u64 fn, u64 addr, u64 range)
{
u8 cc, status;
}
/* Set Interruption Controls */
-void set_irq_ctrl(u16 ctl, char *unused, u8 isc)
+void zpci_set_irq_ctrl(u16 ctl, char *unused, u8 isc)
{
asm volatile (
" .insn rsy,0xeb00000000d1,%[ctl],%[isc],%[u]\n"
return cc;
}
-int s390pci_load(u64 *data, u64 req, u64 offset)
+int zpci_load(u64 *data, u64 req, u64 offset)
{
u8 status;
int cc;
__func__, cc, status, req, offset);
return (cc > 0) ? -EIO : cc;
}
-EXPORT_SYMBOL_GPL(s390pci_load);
+EXPORT_SYMBOL_GPL(zpci_load);
/* PCI Store */
static inline int __pcistg(u64 data, u64 req, u64 offset, u8 *status)
return cc;
}
-int s390pci_store(u64 data, u64 req, u64 offset)
+int zpci_store(u64 data, u64 req, u64 offset)
{
u8 status;
int cc;
__func__, cc, status, req, offset);
return (cc > 0) ? -EIO : cc;
}
-EXPORT_SYMBOL_GPL(s390pci_store);
+EXPORT_SYMBOL_GPL(zpci_store);
/* PCI Store Block */
static inline int __pcistb(const u64 *data, u64 req, u64 offset, u8 *status)
return cc;
}
-int s390pci_store_block(const u64 *data, u64 req, u64 offset)
+int zpci_store_block(const u64 *data, u64 req, u64 offset)
{
u8 status;
int cc;
__func__, cc, status, req, offset);
return (cc > 0) ? -EIO : cc;
}
-EXPORT_SYMBOL_GPL(s390pci_store_block);
+EXPORT_SYMBOL_GPL(zpci_store_block);
+++ /dev/null
-/*
- * Copyright IBM Corp. 2012
- *
- * Author(s):
- * Jan Glauber <jang@linux.vnet.ibm.com>
- */
-
-#define COMPONENT "zPCI"
-#define pr_fmt(fmt) COMPONENT ": " fmt
-
-#include <linux/kernel.h>
-#include <linux/err.h>
-#include <linux/rculist.h>
-#include <linux/hash.h>
-#include <linux/pci.h>
-#include <linux/msi.h>
-#include <asm/hw_irq.h>
-
-/* mapping of irq numbers to msi_desc */
-static struct hlist_head *msi_hash;
-static const unsigned int msi_hash_bits = 8;
-#define MSI_HASH_BUCKETS (1U << msi_hash_bits)
-#define msi_hashfn(nr) hash_long(nr, msi_hash_bits)
-
-static DEFINE_SPINLOCK(msi_map_lock);
-
-struct msi_desc *__irq_get_msi_desc(unsigned int irq)
-{
- struct msi_map *map;
-
- hlist_for_each_entry_rcu(map,
- &msi_hash[msi_hashfn(irq)], msi_chain)
- if (map->irq == irq)
- return map->msi;
- return NULL;
-}
-
-int zpci_msi_set_mask_bits(struct msi_desc *msi, u32 mask, u32 flag)
-{
- if (msi->msi_attrib.is_msix) {
- int offset = msi->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE +
- PCI_MSIX_ENTRY_VECTOR_CTRL;
- msi->masked = readl(msi->mask_base + offset);
- writel(flag, msi->mask_base + offset);
- } else {
- if (msi->msi_attrib.maskbit) {
- int pos;
- u32 mask_bits;
-
- pos = (long) msi->mask_base;
- pci_read_config_dword(msi->dev, pos, &mask_bits);
- mask_bits &= ~(mask);
- mask_bits |= flag & mask;
- pci_write_config_dword(msi->dev, pos, mask_bits);
- } else {
- return 0;
- }
- }
-
- msi->msi_attrib.maskbit = !!flag;
- return 1;
-}
-
-int zpci_setup_msi_irq(struct zpci_dev *zdev, struct msi_desc *msi,
- unsigned int nr, int offset)
-{
- struct msi_map *map;
- struct msi_msg msg;
- int rc;
-
- map = kmalloc(sizeof(*map), GFP_KERNEL);
- if (map == NULL)
- return -ENOMEM;
-
- map->irq = nr;
- map->msi = msi;
- zdev->msi_map[nr & ZPCI_MSI_MASK] = map;
- INIT_HLIST_NODE(&map->msi_chain);
-
- pr_debug("%s hashing irq: %u to bucket nr: %llu\n",
- __func__, nr, msi_hashfn(nr));
- hlist_add_head_rcu(&map->msi_chain, &msi_hash[msi_hashfn(nr)]);
-
- spin_lock(&msi_map_lock);
- rc = irq_set_msi_desc(nr, msi);
- if (rc) {
- spin_unlock(&msi_map_lock);
- hlist_del_rcu(&map->msi_chain);
- kfree(map);
- zdev->msi_map[nr & ZPCI_MSI_MASK] = NULL;
- return rc;
- }
- spin_unlock(&msi_map_lock);
-
- msg.data = nr - offset;
- msg.address_lo = zdev->msi_addr & 0xffffffff;
- msg.address_hi = zdev->msi_addr >> 32;
- write_msi_msg(nr, &msg);
- return 0;
-}
-
-void zpci_teardown_msi_irq(struct zpci_dev *zdev, struct msi_desc *msi)
-{
- int irq = msi->irq & ZPCI_MSI_MASK;
- struct msi_map *map;
-
- msi->msg.address_lo = 0;
- msi->msg.address_hi = 0;
- msi->msg.data = 0;
- msi->irq = 0;
- zpci_msi_set_mask_bits(msi, 1, 1);
-
- spin_lock(&msi_map_lock);
- map = zdev->msi_map[irq];
- hlist_del_rcu(&map->msi_chain);
- kfree(map);
- zdev->msi_map[irq] = NULL;
- spin_unlock(&msi_map_lock);
-}
-
-/*
- * The msi hash table has 256 entries which is good for 4..20
- * devices (a typical device allocates 10 + CPUs MSI's). Maybe make
- * the hash table size adjustable later.
- */
-int __init zpci_msihash_init(void)
-{
- unsigned int i;
-
- msi_hash = kmalloc(MSI_HASH_BUCKETS * sizeof(*msi_hash), GFP_KERNEL);
- if (!msi_hash)
- return -ENOMEM;
-
- for (i = 0; i < MSI_HASH_BUCKETS; i++)
- INIT_HLIST_HEAD(&msi_hash[i]);
- return 0;
-}
-
-void __init zpci_msihash_exit(void)
-{
- kfree(msi_hash);
-}
}
static DEVICE_ATTR(pfgid, S_IRUGO, show_pfgid, NULL);
+static void recover_callback(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct zpci_dev *zdev = get_zdev(pdev);
+ int ret;
+
+ pci_stop_and_remove_bus_device(pdev);
+ ret = zpci_disable_device(zdev);
+ if (ret)
+ return;
+
+ ret = zpci_enable_device(zdev);
+ if (ret)
+ return;
+
+ pci_rescan_bus(zdev->bus);
+}
+
+static ssize_t store_recover(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int rc = device_schedule_callback(dev, recover_callback);
+ return rc ? rc : count;
+}
+static DEVICE_ATTR(recover, S_IWUSR, NULL, store_recover);
+
static struct device_attribute *zpci_dev_attrs[] = {
&dev_attr_function_id,
&dev_attr_function_handle,
&dev_attr_pchid,
&dev_attr_pfgid,
+ &dev_attr_recover,
NULL,
};
def_bool y
depends on 64BIT
select X86_DEV_DMA_OPS
+ select ARCH_USE_CMPXCHG_LOCKREF
### Arch settings
config X86
*/
if (boot_params->sentinel) {
/* fields in boot_params are left uninitialized, clear them */
- memset(&boot_params->olpc_ofw_header, 0,
+ memset(&boot_params->ext_ramdisk_image, 0,
(char *)&boot_params->efi_info -
- (char *)&boot_params->olpc_ofw_header);
+ (char *)&boot_params->ext_ramdisk_image);
memset(&boot_params->kbd_status, 0,
(char *)&boot_params->hdr -
(char *)&boot_params->kbd_status);
extern int __apply_microcode_amd(struct microcode_amd *mc_amd);
extern int apply_microcode_amd(int cpu);
-extern enum ucode_state load_microcode_amd(int cpu, const u8 *data, size_t size);
+extern enum ucode_state load_microcode_amd(u8 family, const u8 *data, size_t size);
#ifdef CONFIG_MICROCODE_AMD_EARLY
#ifdef CONFIG_X86_32
# define UNLOCK_LOCK_PREFIX
#endif
+static __always_inline int arch_spin_value_unlocked(arch_spinlock_t lock)
+{
+ return lock.tickets.head == lock.tickets.tail;
+}
+
/*
* Ticket locks are conceptually two parts, one indicating the current head of
* the queue, and the other indicating the current tail. The lock is acquired
static const int amd_erratum_383[];
static const int amd_erratum_400[];
-static bool cpu_has_amd_erratum(const int *erratum);
+static bool cpu_has_amd_erratum(struct cpuinfo_x86 *cpu, const int *erratum);
static void init_amd(struct cpuinfo_x86 *c)
{
value &= ~(1ULL << 24);
wrmsrl_safe(MSR_AMD64_BU_CFG2, value);
- if (cpu_has_amd_erratum(amd_erratum_383))
+ if (cpu_has_amd_erratum(c, amd_erratum_383))
set_cpu_bug(c, X86_BUG_AMD_TLB_MMATCH);
}
- if (cpu_has_amd_erratum(amd_erratum_400))
+ if (cpu_has_amd_erratum(c, amd_erratum_400))
set_cpu_bug(c, X86_BUG_AMD_APIC_C1E);
rdmsr_safe(MSR_AMD64_PATCH_LEVEL, &c->microcode, &dummy);
static const int amd_erratum_383[] =
AMD_OSVW_ERRATUM(3, AMD_MODEL_RANGE(0x10, 0, 0, 0xff, 0xf));
-static bool cpu_has_amd_erratum(const int *erratum)
+
+static bool cpu_has_amd_erratum(struct cpuinfo_x86 *cpu, const int *erratum)
{
- struct cpuinfo_x86 *cpu = __this_cpu_ptr(&cpu_info);
int osvw_id = *erratum++;
u32 range;
u32 ms;
- /*
- * If called early enough that current_cpu_data hasn't been initialized
- * yet, fall back to boot_cpu_data.
- */
- if (cpu->x86 == 0)
- cpu = &boot_cpu_data;
-
- if (cpu->x86_vendor != X86_VENDOR_AMD)
- return false;
-
if (osvw_id >= 0 && osvw_id < 65536 &&
cpu_has(cpu, X86_FEATURE_OSVW)) {
u64 osvw_len;
return 0;
}
-static unsigned int verify_patch_size(int cpu, u32 patch_size,
+static unsigned int verify_patch_size(u8 family, u32 patch_size,
unsigned int size)
{
- struct cpuinfo_x86 *c = &cpu_data(cpu);
u32 max_size;
#define F1XH_MPB_MAX_SIZE 2048
#define F15H_MPB_MAX_SIZE 4096
#define F16H_MPB_MAX_SIZE 3458
- switch (c->x86) {
+ switch (family) {
case 0x14:
max_size = F14H_MPB_MAX_SIZE;
break;
* driver cannot continue functioning normally. In such cases, we tear
* down everything we've used up so far and exit.
*/
-static int verify_and_add_patch(unsigned int cpu, u8 *fw, unsigned int leftover)
+static int verify_and_add_patch(u8 family, u8 *fw, unsigned int leftover)
{
- struct cpuinfo_x86 *c = &cpu_data(cpu);
struct microcode_header_amd *mc_hdr;
struct ucode_patch *patch;
unsigned int patch_size, crnt_size, ret;
/* check if patch is for the current family */
proc_fam = ((proc_fam >> 8) & 0xf) + ((proc_fam >> 20) & 0xff);
- if (proc_fam != c->x86)
+ if (proc_fam != family)
return crnt_size;
if (mc_hdr->nb_dev_id || mc_hdr->sb_dev_id) {
return crnt_size;
}
- ret = verify_patch_size(cpu, patch_size, leftover);
+ ret = verify_patch_size(family, patch_size, leftover);
if (!ret) {
pr_err("Patch-ID 0x%08x: size mismatch.\n", mc_hdr->patch_id);
return crnt_size;
return crnt_size;
}
-static enum ucode_state __load_microcode_amd(int cpu, const u8 *data, size_t size)
+static enum ucode_state __load_microcode_amd(u8 family, const u8 *data,
+ size_t size)
{
enum ucode_state ret = UCODE_ERROR;
unsigned int leftover;
}
while (leftover) {
- crnt_size = verify_and_add_patch(cpu, fw, leftover);
+ crnt_size = verify_and_add_patch(family, fw, leftover);
if (crnt_size < 0)
return ret;
return UCODE_OK;
}
-enum ucode_state load_microcode_amd(int cpu, const u8 *data, size_t size)
+enum ucode_state load_microcode_amd(u8 family, const u8 *data, size_t size)
{
enum ucode_state ret;
/* free old equiv table */
free_equiv_cpu_table();
- ret = __load_microcode_amd(cpu, data, size);
+ ret = __load_microcode_amd(family, data, size);
if (ret != UCODE_OK)
cleanup();
#if defined(CONFIG_MICROCODE_AMD_EARLY) && defined(CONFIG_X86_32)
/* save BSP's matching patch for early load */
- if (cpu_data(cpu).cpu_index == boot_cpu_data.cpu_index) {
- struct ucode_patch *p = find_patch(cpu);
+ if (cpu_data(smp_processor_id()).cpu_index == boot_cpu_data.cpu_index) {
+ struct ucode_patch *p = find_patch(smp_processor_id());
if (p) {
memset(amd_bsp_mpb, 0, MPB_MAX_SIZE);
memcpy(amd_bsp_mpb, p->data, min_t(u32, ksize(p->data),
goto fw_release;
}
- ret = load_microcode_amd(cpu, fw->data, fw->size);
+ ret = load_microcode_amd(c->x86, fw->data, fw->size);
fw_release:
release_firmware(fw);
uci->cpu_sig.sig = cpuid_eax(0x00000001);
}
#else
-static void collect_cpu_info_amd_early(struct cpuinfo_x86 *c,
- struct ucode_cpu_info *uci)
+void load_ucode_amd_ap(void)
{
+ unsigned int cpu = smp_processor_id();
+ struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
u32 rev, eax;
rdmsr(MSR_AMD64_PATCH_LEVEL, rev, eax);
eax = cpuid_eax(0x00000001);
- uci->cpu_sig.sig = eax;
uci->cpu_sig.rev = rev;
- c->microcode = rev;
- c->x86 = ((eax >> 8) & 0xf) + ((eax >> 20) & 0xff);
-}
-
-void load_ucode_amd_ap(void)
-{
- unsigned int cpu = smp_processor_id();
-
- collect_cpu_info_amd_early(&cpu_data(cpu), ucode_cpu_info + cpu);
+ uci->cpu_sig.sig = eax;
if (cpu && !ucode_loaded) {
void *ucode;
return;
ucode = (void *)(initrd_start + ucode_offset);
- if (load_microcode_amd(0, ucode, ucode_size) != UCODE_OK)
+ eax = ((eax >> 8) & 0xf) + ((eax >> 20) & 0xff);
+ if (load_microcode_amd(eax, ucode, ucode_size) != UCODE_OK)
return;
+
ucode_loaded = true;
}
{
enum ucode_state ret;
void *ucode;
+ u32 eax;
+
#ifdef CONFIG_X86_32
unsigned int bsp = boot_cpu_data.cpu_index;
struct ucode_cpu_info *uci = ucode_cpu_info + bsp;
return 0;
ucode = (void *)(initrd_start + ucode_offset);
- ret = load_microcode_amd(0, ucode, ucode_size);
+ eax = cpuid_eax(0x00000001);
+ eax = ((eax >> 8) & 0xf) + ((eax >> 20) & 0xff);
+
+ ret = load_microcode_amd(eax, ucode, ucode_size);
if (ret != UCODE_OK)
return -EINVAL;
*begin = new_begin;
}
} else {
- *begin = mmap_legacy_base();
+ *begin = current->mm->mmap_legacy_base;
*end = TASK_SIZE;
}
}
return __va(pfn << PAGE_SHIFT);
}
-/* need 4 4k for initial PMD_SIZE, 4k for 0-ISA_END_ADDRESS */
-#define INIT_PGT_BUF_SIZE (5 * PAGE_SIZE)
+/* need 3 4k for initial PMD_SIZE, 3 4k for 0-ISA_END_ADDRESS */
+#define INIT_PGT_BUF_SIZE (6 * PAGE_SIZE)
RESERVE_BRK(early_pgt_alloc, INIT_PGT_BUF_SIZE);
void __init early_alloc_pgt_buf(void)
{
* Bottom-up (legacy) layout on X86_32 did not support randomization, X86_64
* does, but not when emulating X86_32
*/
-unsigned long mmap_legacy_base(void)
+static unsigned long mmap_legacy_base(void)
{
if (mmap_is_ia32())
return TASK_UNMAPPED_BASE;
*/
void arch_pick_mmap_layout(struct mm_struct *mm)
{
+ mm->mmap_legacy_base = mmap_legacy_base();
+ mm->mmap_base = mmap_base();
+
if (mmap_is_legacy()) {
- mm->mmap_base = mmap_legacy_base();
+ mm->mmap_base = mm->mmap_legacy_base;
mm->get_unmapped_area = arch_get_unmapped_area;
} else {
- mm->mmap_base = mmap_base();
mm->get_unmapped_area = arch_get_unmapped_area_topdown;
}
}
e820_add_region(start, end - start, type);
}
+void xen_ignore_unusable(struct e820entry *list, size_t map_size)
+{
+ struct e820entry *entry;
+ unsigned int i;
+
+ for (i = 0, entry = list; i < map_size; i++, entry++) {
+ if (entry->type == E820_UNUSABLE)
+ entry->type = E820_RAM;
+ }
+}
+
/**
* machine_specific_memory_setup - Hook for machine specific memory setup.
**/
}
BUG_ON(rc);
+ /*
+ * Xen won't allow a 1:1 mapping to be created to UNUSABLE
+ * regions, so if we're using the machine memory map leave the
+ * region as RAM as it is in the pseudo-physical map.
+ *
+ * UNUSABLE regions in domUs are not handled and will need
+ * a patch in the future.
+ */
+ if (xen_initial_domain())
+ xen_ignore_unusable(map, memmap.nr_entries);
+
/* Make sure the Xen-supplied memory map is well-ordered. */
sanitize_e820_map(map, memmap.nr_entries, &memmap.nr_entries);
static int xen_hvm_cpu_up(unsigned int cpu, struct task_struct *tidle)
{
int rc;
- rc = native_cpu_up(cpu, tidle);
- WARN_ON (xen_smp_intr_init(cpu));
+ /*
+ * xen_smp_intr_init() needs to run before native_cpu_up()
+ * so that IPI vectors are set up on the booting CPU before
+ * it is marked online in native_cpu_up().
+ */
+ rc = xen_smp_intr_init(cpu);
+ WARN_ON(rc);
+ if (!rc)
+ rc = native_cpu_up(cpu, tidle);
return rc;
}
device->cap._DDC = 1;
}
- if (acpi_video_init_brightness(device))
- return;
-
if (acpi_video_backlight_support()) {
struct backlight_properties props;
struct pci_dev *pdev;
static int count = 0;
char *name;
+ result = acpi_video_init_brightness(device);
+ if (result)
+ return;
name = kasprintf(GFP_KERNEL, "acpi_video%d", count);
if (!name)
return;
if (result)
printk(KERN_ERR PREFIX "Create sysfs link\n");
- } else {
- /* Remove the brightness object. */
- kfree(device->brightness->levels);
- kfree(device->brightness);
- device->brightness = NULL;
}
}
/* Disable sending Early R_OK.
* With "cached read" HDD testing and multiple ports busy on a SATA
- * host controller, 3726 PMP will very rarely drop a deferred
+ * host controller, 3x26 PMP will very rarely drop a deferred
* R_OK that was intended for the host. Symptom will be all
* 5 drives under test will timeout, get reset, and recover.
*/
- if (vendor == 0x1095 && devid == 0x3726) {
+ if (vendor == 0x1095 && (devid == 0x3726 || devid == 0x3826)) {
u32 reg;
err_mask = sata_pmp_read(&ap->link, PMP_GSCR_SII_POL, ®);
if (err_mask) {
rc = -EIO;
- reason = "failed to read Sil3726 Private Register";
+ reason = "failed to read Sil3x26 Private Register";
goto fail;
}
reg &= ~0x1;
err_mask = sata_pmp_write(&ap->link, PMP_GSCR_SII_POL, reg);
if (err_mask) {
rc = -EIO;
- reason = "failed to write Sil3726 Private Register";
+ reason = "failed to write Sil3x26 Private Register";
goto fail;
}
}
u16 devid = sata_pmp_gscr_devid(gscr);
struct ata_link *link;
- if (vendor == 0x1095 && devid == 0x3726) {
- /* sil3726 quirks */
+ if (vendor == 0x1095 && (devid == 0x3726 || devid == 0x3826)) {
+ /* sil3x26 quirks */
ata_for_each_link(link, ap, EDGE) {
/* link reports offline after LPM */
link->flags |= ATA_LFLAG_NO_LPM;
{
struct sata_fsl_host_priv *host_priv = host->private_data;
void __iomem *hcr_base = host_priv->hcr_base;
+ unsigned long flags;
if (count > ICC_MAX_INT_COUNT_THRESHOLD)
count = ICC_MAX_INT_COUNT_THRESHOLD;
(count > ICC_MIN_INT_COUNT_THRESHOLD))
ticks = ICC_SAFE_INT_TICKS;
- spin_lock(&host->lock);
+ spin_lock_irqsave(&host->lock, flags);
iowrite32((count << 24 | ticks), hcr_base + ICC);
intr_coalescing_count = count;
intr_coalescing_ticks = ticks;
- spin_unlock(&host->lock);
+ spin_unlock_irqrestore(&host->lock, flags);
DPRINTK("interrupt coalescing, count = 0x%x, ticks = %x\n",
intr_coalescing_count, intr_coalescing_ticks);
#define SGPIO_SIGNALS 3
#define ECX_ACTIVITY_BITS 0x300000
-#define ECX_ACTIVITY_SHIFT 2
+#define ECX_ACTIVITY_SHIFT 0
#define ECX_LOCATE_BITS 0x80000
#define ECX_LOCATE_SHIFT 1
#define ECX_FAULT_BITS 0x400000
-#define ECX_FAULT_SHIFT 0
+#define ECX_FAULT_SHIFT 2
static inline int sgpio_bit_shift(struct ecx_plat_data *pdata, u32 port,
u32 shift)
{
container_of(dev, struct memory_block, dev);
for (i = 0; i < sections_per_block; i++) {
+ if (!present_section_nr(mem->start_section_nr + i))
+ continue;
pfn = section_nr_to_pfn(mem->start_section_nr + i);
ret &= is_mem_section_removable(pfn, PAGES_PER_SECTION);
}
void *cache;
u32 cache_dirty;
- unsigned long *cache_present;
- unsigned int cache_present_nbits;
-
struct reg_default *patch;
int patch_regs;
unsigned int reg, unsigned int value);
int regcache_sync(struct regmap *map);
int regcache_sync_block(struct regmap *map, void *block,
+ unsigned long *cache_present,
unsigned int block_base, unsigned int start,
unsigned int end);
bool regcache_set_val(struct regmap *map, void *base, unsigned int idx,
unsigned int val);
int regcache_lookup_reg(struct regmap *map, unsigned int reg);
-int regcache_set_reg_present(struct regmap *map, unsigned int reg);
-
-static inline bool regcache_reg_present(struct regmap *map, unsigned int reg)
-{
- if (!map->cache_present)
- return true;
- if (reg > map->cache_present_nbits)
- return false;
- return map->cache_present[BIT_WORD(reg)] & BIT_MASK(reg);
-}
int _regmap_raw_write(struct regmap *map, unsigned int reg,
const void *val, size_t val_len, bool async);
unsigned int base_reg;
/* block of adjacent registers */
void *block;
+ /* Which registers are present */
+ long *cache_present;
/* number of registers available in the block */
unsigned int blklen;
} __attribute__ ((packed));
struct regcache_rbtree_node *rbnode,
unsigned int idx, unsigned int val)
{
+ set_bit(idx, rbnode->cache_present);
regcache_set_val(map, rbnode->block, idx, val);
}
map->lock(map->lock_arg);
mem_size = sizeof(*rbtree_ctx);
- mem_size += BITS_TO_LONGS(map->cache_present_nbits) * sizeof(long);
for (node = rb_first(&rbtree_ctx->root); node != NULL;
node = rb_next(node)) {
n = container_of(node, struct regcache_rbtree_node, node);
mem_size += sizeof(*n);
mem_size += (n->blklen * map->cache_word_size);
+ mem_size += BITS_TO_LONGS(n->blklen) * sizeof(long);
regcache_rbtree_get_base_top_reg(map, n, &base, &top);
this_registers = ((top - base) / map->reg_stride) + 1;
rbtree_node = rb_entry(next, struct regcache_rbtree_node, node);
next = rb_next(&rbtree_node->node);
rb_erase(&rbtree_node->node, &rbtree_ctx->root);
+ kfree(rbtree_node->cache_present);
kfree(rbtree_node->block);
kfree(rbtree_node);
}
rbnode = regcache_rbtree_lookup(map, reg);
if (rbnode) {
reg_tmp = (reg - rbnode->base_reg) / map->reg_stride;
- if (!regcache_reg_present(map, reg))
+ if (!test_bit(reg_tmp, rbnode->cache_present))
return -ENOENT;
*value = regcache_rbtree_get_register(map, rbnode, reg_tmp);
} else {
static int regcache_rbtree_insert_to_block(struct regmap *map,
struct regcache_rbtree_node *rbnode,
- unsigned int pos, unsigned int reg,
+ unsigned int base_reg,
+ unsigned int top_reg,
+ unsigned int reg,
unsigned int value)
{
+ unsigned int blklen;
+ unsigned int pos, offset;
+ unsigned long *present;
u8 *blk;
+ blklen = (top_reg - base_reg) / map->reg_stride + 1;
+ pos = (reg - base_reg) / map->reg_stride;
+ offset = (rbnode->base_reg - base_reg) / map->reg_stride;
+
blk = krealloc(rbnode->block,
- (rbnode->blklen + 1) * map->cache_word_size,
+ blklen * map->cache_word_size,
GFP_KERNEL);
if (!blk)
return -ENOMEM;
+ present = krealloc(rbnode->cache_present,
+ BITS_TO_LONGS(blklen) * sizeof(*present), GFP_KERNEL);
+ if (!present) {
+ kfree(blk);
+ return -ENOMEM;
+ }
+
/* insert the register value in the correct place in the rbnode block */
- memmove(blk + (pos + 1) * map->cache_word_size,
- blk + pos * map->cache_word_size,
- (rbnode->blklen - pos) * map->cache_word_size);
+ if (pos == 0) {
+ memmove(blk + offset * map->cache_word_size,
+ blk, rbnode->blklen * map->cache_word_size);
+ bitmap_shift_right(present, present, offset, blklen);
+ }
/* update the rbnode block, its size and the base register */
rbnode->block = blk;
- rbnode->blklen++;
- if (!pos)
- rbnode->base_reg = reg;
+ rbnode->blklen = blklen;
+ rbnode->base_reg = base_reg;
+ rbnode->cache_present = present;
regcache_rbtree_set_register(map, rbnode, pos, value);
return 0;
if (i != map->rd_table->n_yes_ranges) {
range = &map->rd_table->yes_ranges[i];
- rbnode->blklen = range->range_max - range->range_min
- + 1;
+ rbnode->blklen = (range->range_max - range->range_min) /
+ map->reg_stride + 1;
rbnode->base_reg = range->range_min;
}
}
if (!rbnode->blklen) {
- rbnode->blklen = sizeof(*rbnode);
+ rbnode->blklen = 1;
rbnode->base_reg = reg;
}
rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size,
GFP_KERNEL);
- if (!rbnode->block) {
- kfree(rbnode);
- return NULL;
- }
+ if (!rbnode->block)
+ goto err_free;
+
+ rbnode->cache_present = kzalloc(BITS_TO_LONGS(rbnode->blklen) *
+ sizeof(*rbnode->cache_present), GFP_KERNEL);
+ if (!rbnode->cache_present)
+ goto err_free_block;
return rbnode;
+
+err_free_block:
+ kfree(rbnode->block);
+err_free:
+ kfree(rbnode);
+ return NULL;
}
static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
struct regcache_rbtree_node *rbnode, *rbnode_tmp;
struct rb_node *node;
unsigned int reg_tmp;
- unsigned int pos;
- int i;
int ret;
rbtree_ctx = map->cache;
- /* update the reg_present bitmap, make space if necessary */
- ret = regcache_set_reg_present(map, reg);
- if (ret < 0)
- return ret;
/* if we can't locate it in the cached rbnode we'll have
* to traverse the rbtree looking for it.
reg_tmp = (reg - rbnode->base_reg) / map->reg_stride;
regcache_rbtree_set_register(map, rbnode, reg_tmp, value);
} else {
+ unsigned int base_reg, top_reg;
+ unsigned int new_base_reg, new_top_reg;
+ unsigned int min, max;
+ unsigned int max_dist;
+
+ max_dist = map->reg_stride * sizeof(*rbnode_tmp) /
+ map->cache_word_size;
+ if (reg < max_dist)
+ min = 0;
+ else
+ min = reg - max_dist;
+ max = reg + max_dist;
+
/* look for an adjacent register to the one we are about to add */
for (node = rb_first(&rbtree_ctx->root); node;
node = rb_next(node)) {
rbnode_tmp = rb_entry(node, struct regcache_rbtree_node,
node);
- for (i = 0; i < rbnode_tmp->blklen; i++) {
- reg_tmp = rbnode_tmp->base_reg +
- (i * map->reg_stride);
- if (abs(reg_tmp - reg) != map->reg_stride)
- continue;
- /* decide where in the block to place our register */
- if (reg_tmp + map->reg_stride == reg)
- pos = i + 1;
- else
- pos = i;
- ret = regcache_rbtree_insert_to_block(map,
- rbnode_tmp,
- pos, reg,
- value);
- if (ret)
- return ret;
- rbtree_ctx->cached_rbnode = rbnode_tmp;
- return 0;
+
+ regcache_rbtree_get_base_top_reg(map, rbnode_tmp,
+ &base_reg, &top_reg);
+
+ if (base_reg <= max && top_reg >= min) {
+ new_base_reg = min(reg, base_reg);
+ new_top_reg = max(reg, top_reg);
+ } else {
+ continue;
}
+
+ ret = regcache_rbtree_insert_to_block(map, rbnode_tmp,
+ new_base_reg,
+ new_top_reg, reg,
+ value);
+ if (ret)
+ return ret;
+ rbtree_ctx->cached_rbnode = rbnode_tmp;
+ return 0;
}
/* We did not manage to find a place to insert it in
struct regcache_rbtree_ctx *rbtree_ctx;
struct rb_node *node;
struct regcache_rbtree_node *rbnode;
+ unsigned int base_reg, top_reg;
+ unsigned int start, end;
int ret;
- int base, end;
rbtree_ctx = map->cache;
for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) {
rbnode = rb_entry(node, struct regcache_rbtree_node, node);
- if (rbnode->base_reg > max)
+ regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg,
+ &top_reg);
+ if (base_reg > max)
break;
- if (rbnode->base_reg + rbnode->blklen < min)
+ if (top_reg < min)
continue;
- if (min > rbnode->base_reg)
- base = min - rbnode->base_reg;
+ if (min > base_reg)
+ start = (min - base_reg) / map->reg_stride;
else
- base = 0;
+ start = 0;
- if (max < rbnode->base_reg + rbnode->blklen)
- end = max - rbnode->base_reg + 1;
+ if (max < top_reg)
+ end = (max - base_reg) / map->reg_stride + 1;
else
end = rbnode->blklen;
- ret = regcache_sync_block(map, rbnode->block, rbnode->base_reg,
- base, end);
+ ret = regcache_sync_block(map, rbnode->block,
+ rbnode->cache_present,
+ rbnode->base_reg, start, end);
if (ret != 0)
return ret;
}
return regmap_async_complete(map);
}
+static int regcache_rbtree_drop(struct regmap *map, unsigned int min,
+ unsigned int max)
+{
+ struct regcache_rbtree_ctx *rbtree_ctx;
+ struct regcache_rbtree_node *rbnode;
+ struct rb_node *node;
+ unsigned int base_reg, top_reg;
+ unsigned int start, end;
+
+ rbtree_ctx = map->cache;
+ for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) {
+ rbnode = rb_entry(node, struct regcache_rbtree_node, node);
+
+ regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg,
+ &top_reg);
+ if (base_reg > max)
+ break;
+ if (top_reg < min)
+ continue;
+
+ if (min > base_reg)
+ start = (min - base_reg) / map->reg_stride;
+ else
+ start = 0;
+
+ if (max < top_reg)
+ end = (max - base_reg) / map->reg_stride + 1;
+ else
+ end = rbnode->blklen;
+
+ bitmap_clear(rbnode->cache_present, start, end - start);
+ }
+
+ return 0;
+}
+
struct regcache_ops regcache_rbtree_ops = {
.type = REGCACHE_RBTREE,
.name = "rbtree",
.exit = regcache_rbtree_exit,
.read = regcache_rbtree_read,
.write = regcache_rbtree_write,
- .sync = regcache_rbtree_sync
+ .sync = regcache_rbtree_sync,
+ .drop = regcache_rbtree_drop,
};
map->reg_defaults_raw = config->reg_defaults_raw;
map->cache_word_size = DIV_ROUND_UP(config->val_bits, 8);
map->cache_size_raw = map->cache_word_size * config->num_reg_defaults_raw;
- map->cache_present = NULL;
- map->cache_present_nbits = 0;
map->cache = NULL;
map->cache_ops = cache_types[i];
BUG_ON(!map->cache_ops);
- kfree(map->cache_present);
kfree(map->reg_defaults);
if (map->cache_free)
kfree(map->reg_defaults_raw);
BUG_ON(!map->cache_ops);
- if (!regmap_writeable(map, reg))
- return -EIO;
-
if (!regmap_volatile(map, reg))
return map->cache_ops->write(map, reg, value);
int regcache_drop_region(struct regmap *map, unsigned int min,
unsigned int max)
{
- unsigned int reg;
int ret = 0;
- if (!map->cache_present && !(map->cache_ops && map->cache_ops->drop))
+ if (!map->cache_ops || !map->cache_ops->drop)
return -EINVAL;
map->lock(map->lock_arg);
trace_regcache_drop_region(map->dev, min, max);
- if (map->cache_present)
- for (reg = min; reg < max + 1; reg++)
- clear_bit(reg, map->cache_present);
-
- if (map->cache_ops && map->cache_ops->drop)
- ret = map->cache_ops->drop(map, min, max);
+ ret = map->cache_ops->drop(map, min, max);
map->unlock(map->lock_arg);
}
EXPORT_SYMBOL_GPL(regcache_cache_bypass);
-int regcache_set_reg_present(struct regmap *map, unsigned int reg)
-{
- unsigned long *cache_present;
- unsigned int cache_present_size;
- unsigned int nregs;
- int i;
-
- nregs = reg + 1;
- cache_present_size = BITS_TO_LONGS(nregs);
- cache_present_size *= sizeof(long);
-
- if (!map->cache_present) {
- cache_present = kmalloc(cache_present_size, GFP_KERNEL);
- if (!cache_present)
- return -ENOMEM;
- bitmap_zero(cache_present, nregs);
- map->cache_present = cache_present;
- map->cache_present_nbits = nregs;
- }
-
- if (nregs > map->cache_present_nbits) {
- cache_present = krealloc(map->cache_present,
- cache_present_size, GFP_KERNEL);
- if (!cache_present)
- return -ENOMEM;
- for (i = 0; i < nregs; i++)
- if (i >= map->cache_present_nbits)
- clear_bit(i, cache_present);
- map->cache_present = cache_present;
- map->cache_present_nbits = nregs;
- }
-
- set_bit(reg, map->cache_present);
- return 0;
-}
-
bool regcache_set_val(struct regmap *map, void *base, unsigned int idx,
unsigned int val)
{
return -ENOENT;
}
+static bool regcache_reg_present(unsigned long *cache_present, unsigned int idx)
+{
+ if (!cache_present)
+ return true;
+
+ return test_bit(idx, cache_present);
+}
+
static int regcache_sync_block_single(struct regmap *map, void *block,
+ unsigned long *cache_present,
unsigned int block_base,
unsigned int start, unsigned int end)
{
for (i = start; i < end; i++) {
regtmp = block_base + (i * map->reg_stride);
- if (!regcache_reg_present(map, regtmp))
+ if (!regcache_reg_present(cache_present, i))
continue;
val = regcache_get_val(map, block, i);
}
static int regcache_sync_block_raw(struct regmap *map, void *block,
+ unsigned long *cache_present,
unsigned int block_base, unsigned int start,
unsigned int end)
{
for (i = start; i < end; i++) {
regtmp = block_base + (i * map->reg_stride);
- if (!regcache_reg_present(map, regtmp)) {
+ if (!regcache_reg_present(cache_present, i)) {
ret = regcache_sync_block_raw_flush(map, &data,
base, regtmp);
if (ret != 0)
}
int regcache_sync_block(struct regmap *map, void *block,
+ unsigned long *cache_present,
unsigned int block_base, unsigned int start,
unsigned int end)
{
if (regmap_can_raw_write(map))
- return regcache_sync_block_raw(map, block, block_base,
- start, end);
+ return regcache_sync_block_raw(map, block, cache_present,
+ block_base, start, end);
else
- return regcache_sync_block_single(map, block, block_base,
- start, end);
+ return regcache_sync_block_single(map, block, cache_present,
+ block_base, start, end);
}
unsigned int reg_offset;
/* Suppress the cache if we're using a subrange */
- if (from)
- return from;
+ if (base)
+ return base;
/*
* If we don't have a cache build one so we don't have to do a
reg, ret);
goto err_alloc;
}
+
+ if (!chip->init_ack_masked)
+ continue;
+
+ /* Ack masked but set interrupts */
+ reg = chip->status_base +
+ (i * map->reg_stride * d->irq_reg_stride);
+ ret = regmap_read(map, reg, &d->status_buf[i]);
+ if (ret != 0) {
+ dev_err(map->dev, "Failed to read IRQ status: %d\n",
+ ret);
+ goto err_alloc;
+ }
+
+ if (d->status_buf[i] && chip->ack_base) {
+ reg = chip->ack_base +
+ (i * map->reg_stride * d->irq_reg_stride);
+ ret = regmap_write(map, reg,
+ d->status_buf[i] & d->mask_buf[i]);
+ if (ret != 0) {
+ dev_err(map->dev, "Failed to ack 0x%x: %d\n",
+ reg, ret);
+ goto err_alloc;
+ }
+ }
}
/* Wake is disabled by default */
}
static void regmap_lock_spinlock(void *__map)
+__acquires(&map->spinlock)
{
struct regmap *map = __map;
unsigned long flags;
}
static void regmap_unlock_spinlock(void *__map)
+__releases(&map->spinlock)
{
struct regmap *map = __map;
spin_unlock_irqrestore(&map->spinlock, map->spinlock_flags);
unsigned win_max = win_min +
config->ranges[j].window_len - 1;
+ /* Allow data window inside its own virtual range */
+ if (j == i)
+ continue;
+
if (range_cfg->range_min <= sel_reg &&
sel_reg <= range_cfg->range_max) {
dev_err(map->dev,
int ret;
void *context = _regmap_map_get_context(map);
+ if (!regmap_writeable(map, reg))
+ return -EIO;
+
if (!map->cache_bypass && !map->defer_caching) {
ret = regcache_write(map, reg, val);
if (ret != 0)
int regmap_register_patch(struct regmap *map, const struct reg_default *regs,
int num_regs)
{
+ struct reg_default *p;
int i, ret;
bool bypass;
- /* If needed the implementation can be extended to support this */
- if (map->patch)
- return -EBUSY;
-
map->lock(map->lock_arg);
bypass = map->cache_bypass;
}
}
- map->patch = kcalloc(num_regs, sizeof(struct reg_default), GFP_KERNEL);
- if (map->patch != NULL) {
- memcpy(map->patch, regs,
- num_regs * sizeof(struct reg_default));
- map->patch_regs = num_regs;
+ p = krealloc(map->patch,
+ sizeof(struct reg_default) * (map->patch_regs + num_regs),
+ GFP_KERNEL);
+ if (p) {
+ memcpy(p + map->patch_regs, regs, num_regs * sizeof(*regs));
+ map->patch = p;
+ map->patch_regs += num_regs;
} else {
ret = -ENOMEM;
}
cpu_dev = &pdev->dev;
cpu_dev->of_node = np;
- cpu_reg = devm_regulator_get(cpu_dev, "cpu0");
+ cpu_reg = devm_regulator_get_optional(cpu_dev, "cpu0");
if (IS_ERR(cpu_reg)) {
/*
* If cpu0 regulator supply node is present, but regulator is
Enable support for the CSR SiRFprimaII DMA engine.
config TI_EDMA
- tristate "TI EDMA support"
+ bool "TI EDMA support"
depends on ARCH_DAVINCI || ARCH_OMAP
select DMA_ENGINE
select DMA_VIRTUAL_CHANNELS
&status))
goto log_fail;
- while (status == SDVO_CMD_STATUS_PENDING && retry--) {
+ while ((status == SDVO_CMD_STATUS_PENDING ||
+ status == SDVO_CMD_STATUS_TARGET_NOT_SPECIFIED) && retry--) {
udelay(15);
if (!psb_intel_sdvo_read_byte(psb_intel_sdvo,
SDVO_I2C_CMD_STATUS,
struct sg_table *sg,
enum dma_data_direction dir)
{
+ struct drm_i915_gem_object *obj = attachment->dmabuf->priv;
+
+ mutex_lock(&obj->base.dev->struct_mutex);
+
dma_unmap_sg(attachment->dev, sg->sgl, sg->nents, dir);
sg_free_table(sg);
kfree(sg);
+
+ i915_gem_object_unpin_pages(obj);
+
+ mutex_unlock(&obj->base.dev->struct_mutex);
}
static void i915_gem_dmabuf_release(struct dma_buf *dma_buf)
will not assert AGPBUSY# and will only
be delivered when out of C3. */
#define INSTPM_FORCE_ORDERING (1<<7) /* GEN6+ */
+#define INSTPM_TLB_INVALIDATE (1<<9)
+#define INSTPM_SYNC_FLUSH (1<<5)
#define ACTHD 0x020c8
#define FW_BLC 0x020d8
#define FW_BLC2 0x020dc
#define EDP_LINK_TRAIN_600MV_0DB_IVB (0x30 <<22)
#define EDP_LINK_TRAIN_600MV_3_5DB_IVB (0x36 <<22)
#define EDP_LINK_TRAIN_800MV_0DB_IVB (0x38 <<22)
-#define EDP_LINK_TRAIN_800MV_3_5DB_IVB (0x33 <<22)
+#define EDP_LINK_TRAIN_800MV_3_5DB_IVB (0x3e <<22)
/* legacy values */
#define EDP_LINK_TRAIN_500MV_0DB_IVB (0x00 <<22)
u32 power_well_driver;
+ int num_transcoders;
+
struct intel_cursor_error_state {
u32 control;
u32 position;
} cursor[I915_MAX_PIPES];
struct intel_pipe_error_state {
- enum transcoder cpu_transcoder;
- u32 conf;
u32 source;
-
- u32 htotal;
- u32 hblank;
- u32 hsync;
- u32 vtotal;
- u32 vblank;
- u32 vsync;
} pipe[I915_MAX_PIPES];
struct intel_plane_error_state {
u32 surface;
u32 tile_offset;
} plane[I915_MAX_PIPES];
+
+ struct intel_transcoder_error_state {
+ enum transcoder cpu_transcoder;
+
+ u32 conf;
+
+ u32 htotal;
+ u32 hblank;
+ u32 hsync;
+ u32 vtotal;
+ u32 vblank;
+ u32 vsync;
+ } transcoder[4];
};
struct intel_display_error_state *
{
drm_i915_private_t *dev_priv = dev->dev_private;
struct intel_display_error_state *error;
- enum transcoder cpu_transcoder;
+ int transcoders[] = {
+ TRANSCODER_A,
+ TRANSCODER_B,
+ TRANSCODER_C,
+ TRANSCODER_EDP,
+ };
int i;
+ if (INTEL_INFO(dev)->num_pipes == 0)
+ return NULL;
+
error = kmalloc(sizeof(*error), GFP_ATOMIC);
if (error == NULL)
return NULL;
error->power_well_driver = I915_READ(HSW_PWR_WELL_DRIVER);
for_each_pipe(i) {
- cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, i);
- error->pipe[i].cpu_transcoder = cpu_transcoder;
-
if (INTEL_INFO(dev)->gen <= 6 || IS_VALLEYVIEW(dev)) {
error->cursor[i].control = I915_READ(CURCNTR(i));
error->cursor[i].position = I915_READ(CURPOS(i));
error->plane[i].tile_offset = I915_READ(DSPTILEOFF(i));
}
- error->pipe[i].conf = I915_READ(PIPECONF(cpu_transcoder));
error->pipe[i].source = I915_READ(PIPESRC(i));
- error->pipe[i].htotal = I915_READ(HTOTAL(cpu_transcoder));
- error->pipe[i].hblank = I915_READ(HBLANK(cpu_transcoder));
- error->pipe[i].hsync = I915_READ(HSYNC(cpu_transcoder));
- error->pipe[i].vtotal = I915_READ(VTOTAL(cpu_transcoder));
- error->pipe[i].vblank = I915_READ(VBLANK(cpu_transcoder));
- error->pipe[i].vsync = I915_READ(VSYNC(cpu_transcoder));
+ }
+
+ error->num_transcoders = INTEL_INFO(dev)->num_pipes;
+ if (HAS_DDI(dev_priv->dev))
+ error->num_transcoders++; /* Account for eDP. */
+
+ for (i = 0; i < error->num_transcoders; i++) {
+ enum transcoder cpu_transcoder = transcoders[i];
+
+ error->transcoder[i].cpu_transcoder = cpu_transcoder;
+
+ error->transcoder[i].conf = I915_READ(PIPECONF(cpu_transcoder));
+ error->transcoder[i].htotal = I915_READ(HTOTAL(cpu_transcoder));
+ error->transcoder[i].hblank = I915_READ(HBLANK(cpu_transcoder));
+ error->transcoder[i].hsync = I915_READ(HSYNC(cpu_transcoder));
+ error->transcoder[i].vtotal = I915_READ(VTOTAL(cpu_transcoder));
+ error->transcoder[i].vblank = I915_READ(VBLANK(cpu_transcoder));
+ error->transcoder[i].vsync = I915_READ(VSYNC(cpu_transcoder));
}
/* In the code above we read the registers without checking if the power
{
int i;
+ if (!error)
+ return;
+
err_printf(m, "Num Pipes: %d\n", INTEL_INFO(dev)->num_pipes);
if (HAS_POWER_WELL(dev))
err_printf(m, "PWR_WELL_CTL2: %08x\n",
error->power_well_driver);
for_each_pipe(i) {
err_printf(m, "Pipe [%d]:\n", i);
- err_printf(m, " CPU transcoder: %c\n",
- transcoder_name(error->pipe[i].cpu_transcoder));
- err_printf(m, " CONF: %08x\n", error->pipe[i].conf);
err_printf(m, " SRC: %08x\n", error->pipe[i].source);
- err_printf(m, " HTOTAL: %08x\n", error->pipe[i].htotal);
- err_printf(m, " HBLANK: %08x\n", error->pipe[i].hblank);
- err_printf(m, " HSYNC: %08x\n", error->pipe[i].hsync);
- err_printf(m, " VTOTAL: %08x\n", error->pipe[i].vtotal);
- err_printf(m, " VBLANK: %08x\n", error->pipe[i].vblank);
- err_printf(m, " VSYNC: %08x\n", error->pipe[i].vsync);
err_printf(m, "Plane [%d]:\n", i);
err_printf(m, " CNTR: %08x\n", error->plane[i].control);
err_printf(m, " POS: %08x\n", error->cursor[i].position);
err_printf(m, " BASE: %08x\n", error->cursor[i].base);
}
+
+ for (i = 0; i < error->num_transcoders; i++) {
+ err_printf(m, " CPU transcoder: %c\n",
+ transcoder_name(error->transcoder[i].cpu_transcoder));
+ err_printf(m, " CONF: %08x\n", error->transcoder[i].conf);
+ err_printf(m, " HTOTAL: %08x\n", error->transcoder[i].htotal);
+ err_printf(m, " HBLANK: %08x\n", error->transcoder[i].hblank);
+ err_printf(m, " HSYNC: %08x\n", error->transcoder[i].hsync);
+ err_printf(m, " VTOTAL: %08x\n", error->transcoder[i].vtotal);
+ err_printf(m, " VBLANK: %08x\n", error->transcoder[i].vblank);
+ err_printf(m, " VSYNC: %08x\n", error->transcoder[i].vsync);
+ }
}
#endif
I915_WRITE(mmio, (u32)ring->status_page.gfx_addr);
POSTING_READ(mmio);
+
+ /* Flush the TLB for this page */
+ if (INTEL_INFO(dev)->gen >= 6) {
+ u32 reg = RING_INSTPM(ring->mmio_base);
+ I915_WRITE(reg,
+ _MASKED_BIT_ENABLE(INSTPM_TLB_INVALIDATE |
+ INSTPM_SYNC_FLUSH));
+ if (wait_for((I915_READ(reg) & INSTPM_SYNC_FLUSH) == 0,
+ 1000))
+ DRM_ERROR("%s: wait for SyncFlush to complete for TLB invalidation timed out\n",
+ ring->name);
+ }
}
static int
u32 splitoff;
u32 s, e;
+ BUG_ON(!type);
+
list_for_each_entry(this, &mm->free, fl_entry) {
e = this->offset + this->length;
s = this->offset;
struct nouveau_mm_node *prev, *this, *next;
u32 mask = align - 1;
+ BUG_ON(!type);
+
list_for_each_entry_reverse(this, &mm->free, fl_entry) {
u32 e = this->offset + this->length;
u32 s = this->offset;
return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_MC];
}
-#define nouveau_mc_create(p,e,o,d) \
- nouveau_mc_create_((p), (e), (o), sizeof(**d), (void **)d)
+#define nouveau_mc_create(p,e,o,m,d) \
+ nouveau_mc_create_((p), (e), (o), (m), sizeof(**d), (void **)d)
#define nouveau_mc_destroy(p) ({ \
struct nouveau_mc *pmc = (p); _nouveau_mc_dtor(nv_object(pmc)); \
})
})
int nouveau_mc_create_(struct nouveau_object *, struct nouveau_object *,
- struct nouveau_oclass *, int, void **);
+ struct nouveau_oclass *, const struct nouveau_mc_intr *,
+ int, void **);
void _nouveau_mc_dtor(struct nouveau_object *);
int _nouveau_mc_init(struct nouveau_object *);
int _nouveau_mc_fini(struct nouveau_object *, bool);
return ret;
switch (pfb914 & 0x00000003) {
- case 0x00000000: pfb->ram->type = NV_MEM_TYPE_DDR1; break;
- case 0x00000001: pfb->ram->type = NV_MEM_TYPE_DDR2; break;
- case 0x00000002: pfb->ram->type = NV_MEM_TYPE_GDDR3; break;
+ case 0x00000000: ram->type = NV_MEM_TYPE_DDR1; break;
+ case 0x00000001: ram->type = NV_MEM_TYPE_DDR2; break;
+ case 0x00000002: ram->type = NV_MEM_TYPE_GDDR3; break;
case 0x00000003: break;
}
- pfb->ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000;
- pfb->ram->parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1;
- pfb->ram->tags = nv_rd32(pfb, 0x100320);
+ ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000;
+ ram->parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1;
+ ram->tags = nv_rd32(pfb, 0x100320);
return 0;
}
if (ret)
return ret;
- pfb->ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000;
- pfb->ram->type = NV_MEM_TYPE_STOLEN;
+ ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000;
+ ram->type = NV_MEM_TYPE_STOLEN;
return 0;
}
struct nouveau_ltcg base;
u32 part_nr;
u32 subp_nr;
- struct nouveau_mm tags;
u32 num_tags;
+ u32 tag_base;
+ struct nouveau_mm tags;
struct nouveau_mm_node *tag_ram;
};
u32 tag_size, tag_margin, tag_align;
int ret;
- nv_wr32(priv, 0x17e8d8, priv->part_nr);
- if (nv_device(pfb)->card_type >= NV_E0)
- nv_wr32(priv, 0x17e000, priv->part_nr);
-
/* tags for 1/4 of VRAM should be enough (8192/4 per GiB of VRAM) */
priv->num_tags = (pfb->ram->size >> 17) / 4;
if (priv->num_tags > (1 << 17))
tag_size += tag_align;
tag_size = (tag_size + 0xfff) >> 12; /* round up */
- ret = nouveau_mm_tail(&pfb->vram, 0, tag_size, tag_size, 1,
+ ret = nouveau_mm_tail(&pfb->vram, 1, tag_size, tag_size, 1,
&priv->tag_ram);
if (ret) {
priv->num_tags = 0;
tag_base += tag_align - 1;
ret = do_div(tag_base, tag_align);
- nv_wr32(priv, 0x17e8d4, tag_base);
+ priv->tag_base = tag_base;
}
ret = nouveau_mm_init(&priv->tags, 0, priv->num_tags, 1);
}
priv->subp_nr = nv_rd32(priv, 0x17e8dc) >> 28;
- nv_mask(priv, 0x17e820, 0x00100000, 0x00000000); /* INTR_EN &= ~0x10 */
-
ret = nvc0_ltcg_init_tag_ram(pfb, priv);
if (ret)
return ret;
nouveau_ltcg_destroy(ltcg);
}
+static int
+nvc0_ltcg_init(struct nouveau_object *object)
+{
+ struct nouveau_ltcg *ltcg = (struct nouveau_ltcg *)object;
+ struct nvc0_ltcg_priv *priv = (struct nvc0_ltcg_priv *)ltcg;
+ int ret;
+
+ ret = nouveau_ltcg_init(ltcg);
+ if (ret)
+ return ret;
+
+ nv_mask(priv, 0x17e820, 0x00100000, 0x00000000); /* INTR_EN &= ~0x10 */
+ nv_wr32(priv, 0x17e8d8, priv->part_nr);
+ if (nv_device(ltcg)->card_type >= NV_E0)
+ nv_wr32(priv, 0x17e000, priv->part_nr);
+ nv_wr32(priv, 0x17e8d4, priv->tag_base);
+ return 0;
+}
+
struct nouveau_oclass
nvc0_ltcg_oclass = {
.handle = NV_SUBDEV(LTCG, 0xc0),
.ofuncs = &(struct nouveau_ofuncs) {
.ctor = nvc0_ltcg_ctor,
.dtor = nvc0_ltcg_dtor,
- .init = _nouveau_ltcg_init,
+ .init = nvc0_ltcg_init,
.fini = _nouveau_ltcg_fini,
},
};
int
nouveau_mc_create_(struct nouveau_object *parent, struct nouveau_object *engine,
- struct nouveau_oclass *oclass, int length, void **pobject)
+ struct nouveau_oclass *oclass,
+ const struct nouveau_mc_intr *intr_map,
+ int length, void **pobject)
{
struct nouveau_device *device = nv_device(parent);
struct nouveau_mc *pmc;
if (ret)
return ret;
+ pmc->intr_map = intr_map;
+
ret = request_irq(device->pdev->irq, nouveau_mc_intr,
IRQF_SHARED, "nouveau", pmc);
if (ret < 0)
struct nv04_mc_priv *priv;
int ret;
- ret = nouveau_mc_create(parent, engine, oclass, &priv);
+ ret = nouveau_mc_create(parent, engine, oclass, nv04_mc_intr, &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
- priv->base.intr_map = nv04_mc_intr;
return 0;
}
struct nv44_mc_priv *priv;
int ret;
- ret = nouveau_mc_create(parent, engine, oclass, &priv);
+ ret = nouveau_mc_create(parent, engine, oclass, nv04_mc_intr, &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
- priv->base.intr_map = nv04_mc_intr;
return 0;
}
struct nv50_mc_priv *priv;
int ret;
- ret = nouveau_mc_create(parent, engine, oclass, &priv);
+ ret = nouveau_mc_create(parent, engine, oclass, nv50_mc_intr, &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
- priv->base.intr_map = nv50_mc_intr;
return 0;
}
struct nv98_mc_priv *priv;
int ret;
- ret = nouveau_mc_create(parent, engine, oclass, &priv);
+ ret = nouveau_mc_create(parent, engine, oclass, nv98_mc_intr, &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
- priv->base.intr_map = nv98_mc_intr;
return 0;
}
struct nvc0_mc_priv *priv;
int ret;
- ret = nouveau_mc_create(parent, engine, oclass, &priv);
+ ret = nouveau_mc_create(parent, engine, oclass, nvc0_mc_intr, &priv);
*pobject = nv_object(priv);
if (ret)
return ret;
- priv->base.intr_map = nvc0_mc_intr;
return 0;
}
regp->ramdac_a34 = 0x1;
}
+static int
+nv_crtc_swap_fbs(struct drm_crtc *crtc, struct drm_framebuffer *old_fb)
+{
+ struct nv04_display *disp = nv04_display(crtc->dev);
+ struct nouveau_framebuffer *nvfb = nouveau_framebuffer(crtc->fb);
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+ int ret;
+
+ ret = nouveau_bo_pin(nvfb->nvbo, TTM_PL_FLAG_VRAM);
+ if (ret == 0) {
+ if (disp->image[nv_crtc->index])
+ nouveau_bo_unpin(disp->image[nv_crtc->index]);
+ nouveau_bo_ref(nvfb->nvbo, &disp->image[nv_crtc->index]);
+ }
+
+ return ret;
+}
+
/**
* Sets up registers for the given mode/adjusted_mode pair.
*
struct drm_device *dev = crtc->dev;
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
struct nouveau_drm *drm = nouveau_drm(dev);
+ int ret;
NV_DEBUG(drm, "CTRC mode on CRTC %d:\n", nv_crtc->index);
drm_mode_debug_printmodeline(adjusted_mode);
+ ret = nv_crtc_swap_fbs(crtc, old_fb);
+ if (ret)
+ return ret;
+
/* unlock must come after turning off FP_TG_CONTROL in output_prepare */
nv_lock_vga_crtc_shadow(dev, nv_crtc->index, -1);
static void nv_crtc_destroy(struct drm_crtc *crtc)
{
+ struct nv04_display *disp = nv04_display(crtc->dev);
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
if (!nv_crtc)
drm_crtc_cleanup(crtc);
+ if (disp->image[nv_crtc->index])
+ nouveau_bo_unpin(disp->image[nv_crtc->index]);
+ nouveau_bo_ref(NULL, &disp->image[nv_crtc->index]);
+
nouveau_bo_unmap(nv_crtc->cursor.nvbo);
nouveau_bo_unpin(nv_crtc->cursor.nvbo);
nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo);
nouveau_hw_load_state_palette(dev, nv_crtc->index, &nv04_display(dev)->mode_reg);
}
+static void
+nv_crtc_disable(struct drm_crtc *crtc)
+{
+ struct nv04_display *disp = nv04_display(crtc->dev);
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+ if (disp->image[nv_crtc->index])
+ nouveau_bo_unpin(disp->image[nv_crtc->index]);
+ nouveau_bo_ref(NULL, &disp->image[nv_crtc->index]);
+}
+
static void
nv_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, uint32_t start,
uint32_t size)
struct drm_framebuffer *drm_fb;
struct nouveau_framebuffer *fb;
int arb_burst, arb_lwm;
- int ret;
NV_DEBUG(drm, "index %d\n", nv_crtc->index);
return 0;
}
-
/* If atomic, we want to switch to the fb we were passed, so
- * now we update pointers to do that. (We don't pin; just
- * assume we're already pinned and update the base address.)
+ * now we update pointers to do that.
*/
if (atomic) {
drm_fb = passed_fb;
} else {
drm_fb = crtc->fb;
fb = nouveau_framebuffer(crtc->fb);
- /* If not atomic, we can go ahead and pin, and unpin the
- * old fb we were passed.
- */
- ret = nouveau_bo_pin(fb->nvbo, TTM_PL_FLAG_VRAM);
- if (ret)
- return ret;
-
- if (passed_fb) {
- struct nouveau_framebuffer *ofb = nouveau_framebuffer(passed_fb);
- nouveau_bo_unpin(ofb->nvbo);
- }
}
nv_crtc->fb.offset = fb->nvbo->bo.offset;
nv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
struct drm_framebuffer *old_fb)
{
+ int ret = nv_crtc_swap_fbs(crtc, old_fb);
+ if (ret)
+ return ret;
return nv04_crtc_do_mode_set_base(crtc, old_fb, x, y, false);
}
.mode_set_base = nv04_crtc_mode_set_base,
.mode_set_base_atomic = nv04_crtc_mode_set_base_atomic,
.load_lut = nv_crtc_gamma_load,
+ .disable = nv_crtc_disable,
};
int
uint32_t saved_vga_font[4][16384];
uint32_t dac_users[4];
struct nouveau_object *core;
+ struct nouveau_bo *image[2];
};
static inline struct nv04_display *
ret = nv50_display_flip_next(crtc, fb, chan, 0);
if (ret)
goto fail_unreserve;
+ } else {
+ struct nv04_display *dispnv04 = nv04_display(dev);
+ nouveau_bo_ref(new_bo, &dispnv04->image[nouveau_crtc(crtc)->index]);
}
ret = nouveau_page_flip_emit(chan, old_bo, new_bo, s, &fence);
if (clk < pll->vco1.max_freq)
pll->vco2.max_freq = 0;
- pclk->pll_calc(pclk, pll, clk, &coef);
+ ret = pclk->pll_calc(pclk, pll, clk, &coef);
if (ret == 0)
return -ERANGE;
WREG32(reg, tmp_); \
} while (0)
#define WREG32_AND(reg, and) WREG32_P(reg, 0, and)
-#define WREG32_OR(reg, or) WREG32_P(reg, or, ~or)
+#define WREG32_OR(reg, or) WREG32_P(reg, or, ~(or))
#define WREG32_PLL_P(reg, val, mask) \
do { \
uint32_t tmp_ = RREG32_PLL(reg); \
return -EINVAL;
}
+ if (bo->tbo.sync_obj) {
+ r = radeon_fence_wait(bo->tbo.sync_obj, false);
+ if (r) {
+ DRM_ERROR("Failed waiting for UVD message (%d)!\n", r);
+ return r;
+ }
+ }
+
r = radeon_bo_kmap(bo, &ptr);
if (r) {
DRM_ERROR("Failed mapping the UVD message (%d)!\n", r);
(const u32)ARRAY_SIZE(r7xx_golden_dyn_gpr_registers));
radeon_program_register_sequence(rdev,
rv730_golden_registers,
- (const u32)ARRAY_SIZE(rv770_golden_registers));
+ (const u32)ARRAY_SIZE(rv730_golden_registers));
radeon_program_register_sequence(rdev,
rv730_mgcg_init,
- (const u32)ARRAY_SIZE(rv770_mgcg_init));
+ (const u32)ARRAY_SIZE(rv730_mgcg_init));
break;
case CHIP_RV710:
radeon_program_register_sequence(rdev,
(const u32)ARRAY_SIZE(r7xx_golden_dyn_gpr_registers));
radeon_program_register_sequence(rdev,
rv710_golden_registers,
- (const u32)ARRAY_SIZE(rv770_golden_registers));
+ (const u32)ARRAY_SIZE(rv710_golden_registers));
radeon_program_register_sequence(rdev,
rv710_mgcg_init,
- (const u32)ARRAY_SIZE(rv770_mgcg_init));
+ (const u32)ARRAY_SIZE(rv710_mgcg_init));
break;
case CHIP_RV740:
radeon_program_register_sequence(rdev,
rv740_golden_registers,
- (const u32)ARRAY_SIZE(rv770_golden_registers));
+ (const u32)ARRAY_SIZE(rv740_golden_registers));
radeon_program_register_sequence(rdev,
rv740_mgcg_init,
- (const u32)ARRAY_SIZE(rv770_mgcg_init));
+ (const u32)ARRAY_SIZE(rv740_mgcg_init));
break;
default:
break;
#include <drm/drmP.h>
#include <drm/ttm/ttm_bo_driver.h>
-#define VMW_PPN_SIZE sizeof(unsigned long)
+#define VMW_PPN_SIZE (sizeof(unsigned long))
+/* A future safe maximum remap size. */
+#define VMW_PPN_PER_REMAP ((31 * 1024) / VMW_PPN_SIZE)
static int vmw_gmr2_bind(struct vmw_private *dev_priv,
struct page *pages[],
{
SVGAFifoCmdDefineGMR2 define_cmd;
SVGAFifoCmdRemapGMR2 remap_cmd;
- uint32_t define_size = sizeof(define_cmd) + 4;
- uint32_t remap_size = VMW_PPN_SIZE * num_pages + sizeof(remap_cmd) + 4;
uint32_t *cmd;
uint32_t *cmd_orig;
+ uint32_t define_size = sizeof(define_cmd) + sizeof(*cmd);
+ uint32_t remap_num = num_pages / VMW_PPN_PER_REMAP + ((num_pages % VMW_PPN_PER_REMAP) > 0);
+ uint32_t remap_size = VMW_PPN_SIZE * num_pages + (sizeof(remap_cmd) + sizeof(*cmd)) * remap_num;
+ uint32_t remap_pos = 0;
+ uint32_t cmd_size = define_size + remap_size;
uint32_t i;
- cmd_orig = cmd = vmw_fifo_reserve(dev_priv, define_size + remap_size);
+ cmd_orig = cmd = vmw_fifo_reserve(dev_priv, cmd_size);
if (unlikely(cmd == NULL))
return -ENOMEM;
define_cmd.gmrId = gmr_id;
define_cmd.numPages = num_pages;
+ *cmd++ = SVGA_CMD_DEFINE_GMR2;
+ memcpy(cmd, &define_cmd, sizeof(define_cmd));
+ cmd += sizeof(define_cmd) / sizeof(*cmd);
+
+ /*
+ * Need to split the command if there are too many
+ * pages that goes into the gmr.
+ */
+
remap_cmd.gmrId = gmr_id;
remap_cmd.flags = (VMW_PPN_SIZE > sizeof(*cmd)) ?
SVGA_REMAP_GMR2_PPN64 : SVGA_REMAP_GMR2_PPN32;
- remap_cmd.offsetPages = 0;
- remap_cmd.numPages = num_pages;
- *cmd++ = SVGA_CMD_DEFINE_GMR2;
- memcpy(cmd, &define_cmd, sizeof(define_cmd));
- cmd += sizeof(define_cmd) / sizeof(uint32);
+ while (num_pages > 0) {
+ unsigned long nr = min(num_pages, (unsigned long)VMW_PPN_PER_REMAP);
+
+ remap_cmd.offsetPages = remap_pos;
+ remap_cmd.numPages = nr;
- *cmd++ = SVGA_CMD_REMAP_GMR2;
- memcpy(cmd, &remap_cmd, sizeof(remap_cmd));
- cmd += sizeof(remap_cmd) / sizeof(uint32);
+ *cmd++ = SVGA_CMD_REMAP_GMR2;
+ memcpy(cmd, &remap_cmd, sizeof(remap_cmd));
+ cmd += sizeof(remap_cmd) / sizeof(*cmd);
- for (i = 0; i < num_pages; ++i) {
- if (VMW_PPN_SIZE <= 4)
- *cmd = page_to_pfn(*pages++);
- else
- *((uint64_t *)cmd) = page_to_pfn(*pages++);
+ for (i = 0; i < nr; ++i) {
+ if (VMW_PPN_SIZE <= 4)
+ *cmd = page_to_pfn(*pages++);
+ else
+ *((uint64_t *)cmd) = page_to_pfn(*pages++);
- cmd += VMW_PPN_SIZE / sizeof(*cmd);
+ cmd += VMW_PPN_SIZE / sizeof(*cmd);
+ }
+
+ num_pages -= nr;
+ remap_pos += nr;
}
- vmw_fifo_commit(dev_priv, define_size + remap_size);
+ BUG_ON(cmd != cmd_orig + cmd_size / sizeof(*cmd));
+
+ vmw_fifo_commit(dev_priv, cmd_size);
return 0;
}
If you say yes here you get support for the temperature
sensor(s) inside your CPU. Supported are later revisions of
the AMD Family 10h and all revisions of the AMD Family 11h,
- 12h (Llano), 14h (Brazos) and 15h (Bulldozer/Trinity)
- microarchitectures.
+ 12h (Llano), 14h (Brazos), 15h (Bulldozer/Trinity) and
+ 16h (Kabini) microarchitectures.
This driver can also be built as a module. If so, the module
will be called k10temp.
This driver can also be built as a module. If so, the module
will be called hih6130.
+config SENSORS_HTU21
+ tristate "Measurement Specialties HTU21D humidity/temperature sensors"
+ depends on I2C
+ help
+ If you say yes here you get support for the Measurement Specialties
+ HTU21D humidity and temperature sensors.
+
+ This driver can also be built as a module. If so, the module
+ will be called htu21.
+
config SENSORS_CORETEMP
tristate "Intel Core/Core2/Atom temperature sensor"
depends on X86
tristate "Texas Instruments ADS1015"
depends on I2C
help
- If you say yes here you get support for Texas Instruments ADS1015
- 12-bit 4-input ADC device.
+ If you say yes here you get support for Texas Instruments
+ ADS1015/ADS1115 12/16-bit 4-input ADC device.
This driver can also be built as a module. If so, the module
will be called ads1015.
obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o
obj-$(CONFIG_SENSORS_GPIO_FAN) += gpio-fan.o
obj-$(CONFIG_SENSORS_HIH6130) += hih6130.o
+obj-$(CONFIG_SENSORS_HTU21) += htu21.o
obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o
obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o
obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o
* A hwmon driver for ACPI 4.0 power meters
* Copyright (C) 2009 IBM
*
- * Author: Darrick J. Wong <djwong@us.ibm.com>
+ * Author: Darrick J. Wong <darrick.wong@oracle.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
acpi_bus_unregister_driver(&acpi_power_meter_driver);
}
-MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>");
+MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>");
MODULE_DESCRIPTION("ACPI 4.0 power meter driver");
MODULE_LICENSE("GPL");
6144, 4096, 2048, 1024, 512, 256, 256, 256 };
/* Data rates in samples per second */
-static const unsigned int data_rate_table[8] = {
- 128, 250, 490, 920, 1600, 2400, 3300, 3300 };
+static const unsigned int data_rate_table_1015[8] = {
+ 128, 250, 490, 920, 1600, 2400, 3300, 3300
+};
+
+static const unsigned int data_rate_table_1115[8] = {
+ 8, 16, 32, 64, 128, 250, 475, 860
+};
#define ADS1015_DEFAULT_CHANNELS 0xff
#define ADS1015_DEFAULT_PGA 2
#define ADS1015_DEFAULT_DATA_RATE 4
+enum ads1015_chips {
+ ads1015,
+ ads1115,
+};
+
struct ads1015_data {
struct device *hwmon_dev;
struct mutex update_lock; /* mutex protect updates */
struct ads1015_channel_data channel_data[ADS1015_CHANNELS];
+ enum ads1015_chips id;
};
static int ads1015_read_adc(struct i2c_client *client, unsigned int channel)
unsigned int pga = data->channel_data[channel].pga;
unsigned int data_rate = data->channel_data[channel].data_rate;
unsigned int conversion_time_ms;
+ const unsigned int * const rate_table = data->id == ads1115 ?
+ data_rate_table_1115 : data_rate_table_1015;
int res;
mutex_lock(&data->update_lock);
if (res < 0)
goto err_unlock;
config = res;
- conversion_time_ms = DIV_ROUND_UP(1000, data_rate_table[data_rate]);
+ conversion_time_ms = DIV_ROUND_UP(1000, rate_table[data_rate]);
/* setup and start single conversion */
config &= 0x001f;
struct ads1015_data *data = i2c_get_clientdata(client);
unsigned int pga = data->channel_data[channel].pga;
int fullscale = fullscale_table[pga];
+ const unsigned mask = data->id == ads1115 ? 0x7fff : 0x7ff0;
- return DIV_ROUND_CLOSEST(reg * fullscale, 0x7ff0);
+ return DIV_ROUND_CLOSEST(reg * fullscale, mask);
}
/* sysfs callback function */
GFP_KERNEL);
if (!data)
return -ENOMEM;
-
+ data->id = id->driver_data;
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
}
static const struct i2c_device_id ads1015_id[] = {
- { "ads1015", 0 },
+ { "ads1015", ads1015},
+ { "ads1115", ads1115},
{ }
};
MODULE_DEVICE_TABLE(i2c, ads1015_id);
static int ads7828_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- struct ads7828_platform_data *pdata = client->dev.platform_data;
+ struct ads7828_platform_data *pdata = dev_get_platdata(&client->dev);
struct ads7828_data *data;
int err;
* A hwmon driver for the Analog Devices ADT7462
* Copyright (C) 2008 IBM
*
- * Author: Darrick J. Wong <djwong@us.ibm.com>
+ * Author: Darrick J. Wong <darrick.wong@oracle.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
return 0x4C;
break;
}
- return -ENODEV;
+ return 0;
}
static int ADT7462_REG_VOLT_MIN(struct adt7462_data *data, int which)
return 0x77;
break;
}
- return -ENODEV;
+ return 0;
}
static int ADT7462_REG_VOLT(struct adt7462_data *data, int which)
module_i2c_driver(adt7462_driver);
-MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>");
+MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>");
MODULE_DESCRIPTION("ADT7462 driver");
MODULE_LICENSE("GPL");
* A hwmon driver for the Analog Devices ADT7470
* Copyright (C) 2007 IBM
*
- * Author: Darrick J. Wong <djwong@us.ibm.com>
+ * Author: Darrick J. Wong <darrick.wong@oracle.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
module_i2c_driver(adt7470_driver);
-MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>");
+MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>");
MODULE_DESCRIPTION("ADT7470 driver");
MODULE_LICENSE("GPL");
return tjmax;
}
+static bool cpu_has_tjmax(struct cpuinfo_x86 *c)
+{
+ u8 model = c->x86_model;
+
+ return model > 0xe &&
+ model != 0x1c &&
+ model != 0x26 &&
+ model != 0x27 &&
+ model != 0x35 &&
+ model != 0x36;
+}
+
static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
{
int err;
*/
err = rdmsr_safe_on_cpu(id, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
if (err) {
- if (c->x86_model > 0xe && c->x86_model != 0x1c)
+ if (cpu_has_tjmax(c))
dev_warn(dev, "Unable to read TjMax from CPU %u\n", id);
} else {
val = (eax >> 16) & 0xff;
static void ds620_init_client(struct i2c_client *client)
{
- struct ds620_platform_data *ds620_info = client->dev.platform_data;
+ struct ds620_platform_data *ds620_info = dev_get_platdata(&client->dev);
u16 conf, new_conf;
new_conf = conf =
static int f71805f_probe(struct platform_device *pdev)
{
- struct f71805f_sio_data *sio_data = pdev->dev.platform_data;
+ struct f71805f_sio_data *sio_data = dev_get_platdata(&pdev->dev);
struct f71805f_data *data;
struct resource *res;
int i, err;
static int f71882fg_probe(struct platform_device *pdev)
{
struct f71882fg_data *data;
- struct f71882fg_sio_data *sio_data = pdev->dev.platform_data;
+ struct f71882fg_sio_data *sio_data = dev_get_platdata(&pdev->dev);
int nr_fans = f71882fg_nr_fans[sio_data->type];
int nr_temps = f71882fg_nr_temps[sio_data->type];
int err, i;
const struct i2c_device_id *id)
{
struct f75375_data *data;
- struct f75375s_platform_data *f75375s_pdata = client->dev.platform_data;
+ struct f75375s_platform_data *f75375s_pdata =
+ dev_get_platdata(&client->dev);
int err;
if (!i2c_check_functionality(client->adapter,
static int g762_pdata_prop_import(struct i2c_client *client)
{
- struct g762_platform_data *pdata = client->dev.platform_data;
+ struct g762_platform_data *pdata = dev_get_platdata(&client->dev);
int ret;
if (!pdata)
{
int err;
struct gpio_fan_data *fan_data;
- struct gpio_fan_platform_data *pdata = pdev->dev.platform_data;
+ struct gpio_fan_platform_data *pdata = dev_get_platdata(&pdev->dev);
#ifdef CONFIG_OF_GPIO
if (!pdata) {
--- /dev/null
+/*
+ * Measurement Specialties HTU21D humidity and temperature sensor driver
+ *
+ * Copyright (C) 2013 William Markezana <william.markezana@meas-spec.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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/jiffies.h>
+
+/* HTU21 Commands */
+#define HTU21_T_MEASUREMENT_HM 0xE3
+#define HTU21_RH_MEASUREMENT_HM 0xE5
+
+struct htu21 {
+ struct device *hwmon_dev;
+ struct mutex lock;
+ bool valid;
+ unsigned long last_update;
+ int temperature;
+ int humidity;
+};
+
+static inline int htu21_temp_ticks_to_millicelsius(int ticks)
+{
+ ticks &= ~0x0003; /* clear status bits */
+ /*
+ * Formula T = -46.85 + 175.72 * ST / 2^16 from datasheet p14,
+ * optimized for integer fixed point (3 digits) arithmetic
+ */
+ return ((21965 * ticks) >> 13) - 46850;
+}
+
+static inline int htu21_rh_ticks_to_per_cent_mille(int ticks)
+{
+ ticks &= ~0x0003; /* clear status bits */
+ /*
+ * Formula RH = -6 + 125 * SRH / 2^16 from datasheet p14,
+ * optimized for integer fixed point (3 digits) arithmetic
+ */
+ return ((15625 * ticks) >> 13) - 6000;
+}
+
+static int htu21_update_measurements(struct i2c_client *client)
+{
+ int ret = 0;
+ struct htu21 *htu21 = i2c_get_clientdata(client);
+
+ mutex_lock(&htu21->lock);
+
+ if (time_after(jiffies, htu21->last_update + HZ / 2) ||
+ !htu21->valid) {
+ ret = i2c_smbus_read_word_swapped(client,
+ HTU21_T_MEASUREMENT_HM);
+ if (ret < 0)
+ goto out;
+ htu21->temperature = htu21_temp_ticks_to_millicelsius(ret);
+ ret = i2c_smbus_read_word_swapped(client,
+ HTU21_RH_MEASUREMENT_HM);
+ if (ret < 0)
+ goto out;
+ htu21->humidity = htu21_rh_ticks_to_per_cent_mille(ret);
+ htu21->last_update = jiffies;
+ htu21->valid = true;
+ }
+out:
+ mutex_unlock(&htu21->lock);
+
+ return ret >= 0 ? 0 : ret;
+}
+
+static ssize_t htu21_show_temperature(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct htu21 *htu21 = i2c_get_clientdata(client);
+ int ret = htu21_update_measurements(client);
+ if (ret < 0)
+ return ret;
+ return sprintf(buf, "%d\n", htu21->temperature);
+}
+
+static ssize_t htu21_show_humidity(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct htu21 *htu21 = i2c_get_clientdata(client);
+ int ret = htu21_update_measurements(client);
+ if (ret < 0)
+ return ret;
+ return sprintf(buf, "%d\n", htu21->humidity);
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
+ htu21_show_temperature, NULL, 0);
+static SENSOR_DEVICE_ATTR(humidity1_input, S_IRUGO,
+ htu21_show_humidity, NULL, 0);
+
+static struct attribute *htu21_attributes[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_humidity1_input.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group htu21_group = {
+ .attrs = htu21_attributes,
+};
+
+static int htu21_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct htu21 *htu21;
+ int err;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_WORD_DATA)) {
+ dev_err(&client->dev,
+ "adapter does not support SMBus word transactions\n");
+ return -ENODEV;
+ }
+
+ htu21 = devm_kzalloc(&client->dev, sizeof(*htu21), GFP_KERNEL);
+ if (!htu21)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, htu21);
+
+ mutex_init(&htu21->lock);
+
+ err = sysfs_create_group(&client->dev.kobj, &htu21_group);
+ if (err) {
+ dev_dbg(&client->dev, "could not create sysfs files\n");
+ return err;
+ }
+ htu21->hwmon_dev = hwmon_device_register(&client->dev);
+ if (IS_ERR(htu21->hwmon_dev)) {
+ dev_dbg(&client->dev, "unable to register hwmon device\n");
+ err = PTR_ERR(htu21->hwmon_dev);
+ goto error;
+ }
+
+ dev_info(&client->dev, "initialized\n");
+
+ return 0;
+
+error:
+ sysfs_remove_group(&client->dev.kobj, &htu21_group);
+ return err;
+}
+
+static int htu21_remove(struct i2c_client *client)
+{
+ struct htu21 *htu21 = i2c_get_clientdata(client);
+
+ hwmon_device_unregister(htu21->hwmon_dev);
+ sysfs_remove_group(&client->dev.kobj, &htu21_group);
+
+ return 0;
+}
+
+static const struct i2c_device_id htu21_id[] = {
+ { "htu21", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, htu21_id);
+
+static struct i2c_driver htu21_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "htu21",
+ },
+ .probe = htu21_probe,
+ .remove = htu21_remove,
+ .id_table = htu21_id,
+};
+
+module_i2c_driver(htu21_driver);
+
+MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>");
+MODULE_DESCRIPTION("MEAS HTU21D humidity and temperature sensor driver");
+MODULE_LICENSE("GPL");
* temperature sensors
* Copyright (C) 2007 IBM
*
- * Author: Darrick J. Wong <djwong@us.ibm.com>
+ * Author: Darrick J. Wong <darrick.wong@oracle.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
platform_driver_unregister(&i5k_amb_driver);
}
-MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>");
+MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>");
MODULE_DESCRIPTION("Intel 5000 chipset FB-DIMM AMB temperature sensor");
MODULE_LICENSE("GPL");
* temperature/power/energy sensors and capping functionality.
* Copyright (C) 2008 IBM
*
- * Author: Darrick J. Wong <djwong@us.ibm.com>
+ * Author: Darrick J. Wong <darrick.wong@oracle.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
aem_delete(p1);
}
-MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>");
+MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>");
MODULE_DESCRIPTION("IBM AEM power/temp/energy sensor driver");
MODULE_LICENSE("GPL");
* A hwmon driver for the IBM PowerExecutive temperature/power sensors
* Copyright (C) 2007 IBM
*
- * Author: Darrick J. Wong <djwong@us.ibm.com>
+ * Author: Darrick J. Wong <darrick.wong@oracle.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
ibmpex_bmc_delete(p);
}
-MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>");
+MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>");
MODULE_DESCRIPTION("IBM PowerExecutive power/temperature sensor driver");
MODULE_LICENSE("GPL");
if (!data)
return -ENOMEM;
- if (client->dev.platform_data) {
+ if (dev_get_platdata(&client->dev)) {
pdata =
- (struct ina2xx_platform_data *)client->dev.platform_data;
+ (struct ina2xx_platform_data *)dev_get_platdata(&client->dev);
shunt = pdata->shunt_uohms;
} else if (!of_property_read_u32(client->dev.of_node,
"shunt-resistor", &val)) {
static void it87_remove_files(struct device *dev)
{
struct it87_data *data = platform_get_drvdata(pdev);
- struct it87_sio_data *sio_data = dev->platform_data;
+ struct it87_sio_data *sio_data = dev_get_platdata(dev);
int i;
sysfs_remove_group(&dev->kobj, &it87_group);
struct it87_data *data;
struct resource *res;
struct device *dev = &pdev->dev;
- struct it87_sio_data *sio_data = dev->platform_data;
+ struct it87_sio_data *sio_data = dev_get_platdata(dev);
int err = 0, i;
int enable_pwm_interface;
int fan_beep_need_rw;
/* Called when we have found a new IT87. */
static void it87_init_device(struct platform_device *pdev)
{
- struct it87_sio_data *sio_data = pdev->dev.platform_data;
+ struct it87_sio_data *sio_data = dev_get_platdata(&pdev->dev);
struct it87_data *data = platform_get_drvdata(pdev);
int tmp, i;
u8 mask;
/*
- * k10temp.c - AMD Family 10h/11h/12h/14h/15h processor hardware monitoring
+ * k10temp.c - AMD Family 10h/11h/12h/14h/15h/16h processor hardware monitoring
*
* Copyright (c) 2009 Clemens Ladisch <clemens@ladisch.de>
*
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CNB17H_F3) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F3) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M10H_F3) },
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_NB_F3) },
{}
};
MODULE_DEVICE_TABLE(pci, k10temp_id_table);
{
struct lm87_data *data = i2c_get_clientdata(client);
- if (client->dev.platform_data) {
- data->channel = *(u8 *)client->dev.platform_data;
+ if (dev_get_platdata(&client->dev)) {
+ data->channel = *(u8 *)dev_get_platdata(&client->dev);
lm87_write_value(client,
LM87_REG_CHANNEL_MODE, data->channel);
} else {
{
int ch, ret;
struct max197_data *data;
- struct max197_platform_data *pdata = pdev->dev.platform_data;
+ struct max197_platform_data *pdata = dev_get_platdata(&pdev->dev);
enum max197_chips chip = platform_get_device_id(pdev)->driver_data;
if (pdata == NULL) {
{
struct max6639_data *data = i2c_get_clientdata(client);
struct max6639_platform_data *max6639_info =
- client->dev.platform_data;
+ dev_get_platdata(&client->dev);
int i;
int rpm_range = 1; /* default: 4000 RPM */
int err;
break;
}
- if (client->dev.platform_data) {
- data->vdd = *(u32 *)client->dev.platform_data;
+ if (dev_get_platdata(&client->dev)) {
+ data->vdd = *(u32 *)dev_get_platdata(&client->dev);
if (data->vdd > MCP3021_VDD_MAX || data->vdd < MCP3021_VDD_MIN)
return -EINVAL;
- } else
+ } else {
data->vdd = MCP3021_VDD_REF;
+ }
err = sysfs_create_file(&client->dev.kobj, &dev_attr_in0_input.attr);
if (err)
* Supports the following chips:
*
* Chip #vin #fan #pwm #temp chip IDs man ID
+ * nct6106d 9 3 3 6+3 0xc450 0xc1 0x5ca3
* nct6775f 9 4 3 6+3 0xb470 0xc1 0x5ca3
* 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
*
* #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 { nct6775, nct6776, nct6779 };
+enum kinds { nct6106, nct6775, nct6776, nct6779, nct6791 };
/* used to set data->name = nct6775_device_names[data->sio_kind] */
static const char * const nct6775_device_names[] = {
+ "nct6106",
"nct6775",
"nct6776",
"nct6779",
+ "nct6791",
};
static unsigned short force_id;
#define SIO_REG_ENABLE 0x30 /* Logical device enable */
#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
+#define SIO_NCT6106_ID 0xc450
#define SIO_NCT6775_ID 0xb470
#define SIO_NCT6776_ID 0xc330
#define SIO_NCT6779_ID 0xc560
+#define SIO_NCT6791_ID 0xc800
#define SIO_ID_MASK 0xFFF0
enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 };
#define NUM_TEMP 10 /* Max number of temp attribute sets w/ limits*/
#define NUM_TEMP_FIXED 6 /* Max number of fixed temp attribute sets */
-#define NUM_REG_ALARM 4 /* Max number of alarm registers */
+#define NUM_REG_ALARM 7 /* Max number of alarm registers */
+#define NUM_REG_BEEP 5 /* Max number of beep registers */
+
+#define NUM_FAN 6
/* Common and NCT6775 specific data */
#define NCT6775_REG_VBAT 0x5D
#define NCT6775_REG_DIODE 0x5E
+#define NCT6775_DIODE_MASK 0x02
#define NCT6775_REG_FANDIV1 0x506
#define NCT6775_REG_FANDIV2 0x507
static const u16 NCT6775_REG_ALARM[NUM_REG_ALARM] = { 0x459, 0x45A, 0x45B };
-/* 0..15 voltages, 16..23 fans, 24..31 temperatures */
+/* 0..15 voltages, 16..23 fans, 24..29 temperatures, 30..31 intrusion */
static const s8 NCT6775_ALARM_BITS[] = {
0, 1, 2, 3, 8, 21, 20, 16, /* in0.. in7 */
#define TEMP_ALARM_BASE 24
#define INTRUSION_ALARM_BASE 30
+static const u16 NCT6775_REG_BEEP[NUM_REG_BEEP] = { 0x56, 0x57, 0x453, 0x4e };
+
+/*
+ * 0..14 voltages, 15 global beep enable, 16..23 fans, 24..29 temperatures,
+ * 30..31 intrusion
+ */
+static const s8 NCT6775_BEEP_BITS[] = {
+ 0, 1, 2, 3, 8, 9, 10, 16, /* in0.. in7 */
+ 17, -1, -1, -1, -1, -1, -1, /* in8..in14 */
+ 21, /* global beep enable */
+ 6, 7, 11, 28, -1, /* fan1..fan5 */
+ -1, -1, -1, /* unused */
+ 4, 5, 13, -1, -1, -1, /* temp1..temp6 */
+ 12, -1 }; /* intrusion0, intrusion1 */
+
+#define BEEP_ENABLE_BASE 15
+
static const u8 NCT6775_REG_CR_CASEOPEN_CLR[] = { 0xe6, 0xee };
static const u8 NCT6775_CR_CASEOPEN_CLR_MASK[] = { 0x20, 0x01 };
/* Advanced Fan control, some values are common for all fans */
-static const u16 NCT6775_REG_TARGET[] = { 0x101, 0x201, 0x301, 0x801, 0x901 };
-static const u16 NCT6775_REG_FAN_MODE[] = { 0x102, 0x202, 0x302, 0x802, 0x902 };
+static const u16 NCT6775_REG_TARGET[] = {
+ 0x101, 0x201, 0x301, 0x801, 0x901, 0xa01 };
+static const u16 NCT6775_REG_FAN_MODE[] = {
+ 0x102, 0x202, 0x302, 0x802, 0x902, 0xa02 };
static const u16 NCT6775_REG_FAN_STEP_DOWN_TIME[] = {
- 0x103, 0x203, 0x303, 0x803, 0x903 };
+ 0x103, 0x203, 0x303, 0x803, 0x903, 0xa03 };
static const u16 NCT6775_REG_FAN_STEP_UP_TIME[] = {
- 0x104, 0x204, 0x304, 0x804, 0x904 };
+ 0x104, 0x204, 0x304, 0x804, 0x904, 0xa04 };
static const u16 NCT6775_REG_FAN_STOP_OUTPUT[] = {
- 0x105, 0x205, 0x305, 0x805, 0x905 };
-static const u16 NCT6775_REG_FAN_START_OUTPUT[]
- = { 0x106, 0x206, 0x306, 0x806, 0x906 };
+ 0x105, 0x205, 0x305, 0x805, 0x905, 0xa05 };
+static const u16 NCT6775_REG_FAN_START_OUTPUT[] = {
+ 0x106, 0x206, 0x306, 0x806, 0x906, 0xa06 };
static const u16 NCT6775_REG_FAN_MAX_OUTPUT[] = { 0x10a, 0x20a, 0x30a };
static const u16 NCT6775_REG_FAN_STEP_OUTPUT[] = { 0x10b, 0x20b, 0x30b };
static const u16 NCT6775_REG_FAN_STOP_TIME[] = {
- 0x107, 0x207, 0x307, 0x807, 0x907 };
-static const u16 NCT6775_REG_PWM[] = { 0x109, 0x209, 0x309, 0x809, 0x909 };
-static const u16 NCT6775_REG_PWM_READ[] = { 0x01, 0x03, 0x11, 0x13, 0x15 };
+ 0x107, 0x207, 0x307, 0x807, 0x907, 0xa07 };
+static const u16 NCT6775_REG_PWM[] = {
+ 0x109, 0x209, 0x309, 0x809, 0x909, 0xa09 };
+static const u16 NCT6775_REG_PWM_READ[] = {
+ 0x01, 0x03, 0x11, 0x13, 0x15, 0xa09 };
static const u16 NCT6775_REG_FAN[] = { 0x630, 0x632, 0x634, 0x636, 0x638 };
static const u16 NCT6775_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d };
static const u16 NCT6775_REG_FAN_PULSES[] = { 0x641, 0x642, 0x643, 0x644, 0 };
+static const u16 NCT6775_FAN_PULSE_SHIFT[] = { 0, 0, 0, 0, 0, 0 };
static const u16 NCT6775_REG_TEMP[] = {
0x27, 0x150, 0x250, 0x62b, 0x62c, 0x62d };
0x621, 0x622, 0x623, 0x624, 0x625, 0x626 };
static const u16 NCT6775_REG_TEMP_SEL[] = {
- 0x100, 0x200, 0x300, 0x800, 0x900 };
+ 0x100, 0x200, 0x300, 0x800, 0x900, 0xa00 };
static const u16 NCT6775_REG_WEIGHT_TEMP_SEL[] = {
- 0x139, 0x239, 0x339, 0x839, 0x939 };
+ 0x139, 0x239, 0x339, 0x839, 0x939, 0xa39 };
static const u16 NCT6775_REG_WEIGHT_TEMP_STEP[] = {
- 0x13a, 0x23a, 0x33a, 0x83a, 0x93a };
+ 0x13a, 0x23a, 0x33a, 0x83a, 0x93a, 0xa3a };
static const u16 NCT6775_REG_WEIGHT_TEMP_STEP_TOL[] = {
- 0x13b, 0x23b, 0x33b, 0x83b, 0x93b };
+ 0x13b, 0x23b, 0x33b, 0x83b, 0x93b, 0xa3b };
static const u16 NCT6775_REG_WEIGHT_DUTY_STEP[] = {
- 0x13c, 0x23c, 0x33c, 0x83c, 0x93c };
+ 0x13c, 0x23c, 0x33c, 0x83c, 0x93c, 0xa3c };
static const u16 NCT6775_REG_WEIGHT_TEMP_BASE[] = {
- 0x13d, 0x23d, 0x33d, 0x83d, 0x93d };
+ 0x13d, 0x23d, 0x33d, 0x83d, 0x93d, 0xa3d };
static const u16 NCT6775_REG_TEMP_OFFSET[] = { 0x454, 0x455, 0x456 };
static const u16 NCT6775_REG_AUTO_TEMP[] = {
- 0x121, 0x221, 0x321, 0x821, 0x921 };
+ 0x121, 0x221, 0x321, 0x821, 0x921, 0xa21 };
static const u16 NCT6775_REG_AUTO_PWM[] = {
- 0x127, 0x227, 0x327, 0x827, 0x927 };
+ 0x127, 0x227, 0x327, 0x827, 0x927, 0xa27 };
#define NCT6775_AUTO_TEMP(data, nr, p) ((data)->REG_AUTO_TEMP[nr] + (p))
#define NCT6775_AUTO_PWM(data, nr, p) ((data)->REG_AUTO_PWM[nr] + (p))
static const u16 NCT6775_REG_CRITICAL_ENAB[] = { 0x134, 0x234, 0x334 };
static const u16 NCT6775_REG_CRITICAL_TEMP[] = {
- 0x135, 0x235, 0x335, 0x835, 0x935 };
+ 0x135, 0x235, 0x335, 0x835, 0x935, 0xa35 };
static const u16 NCT6775_REG_CRITICAL_TEMP_TOLERANCE[] = {
- 0x138, 0x238, 0x338, 0x838, 0x938 };
+ 0x138, 0x238, 0x338, 0x838, 0x938, 0xa38 };
static const char *const nct6775_temp_label[] = {
"",
4, 5, 13, -1, -1, -1, /* temp1..temp6 */
12, 9 }; /* intrusion0, intrusion1 */
+static const u16 NCT6776_REG_BEEP[NUM_REG_BEEP] = { 0xb2, 0xb3, 0xb4, 0xb5 };
+
+static const s8 NCT6776_BEEP_BITS[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, /* in0.. in7 */
+ 8, -1, -1, -1, -1, -1, -1, /* in8..in14 */
+ 24, /* global beep enable */
+ 25, 26, 27, 28, 29, /* fan1..fan5 */
+ -1, -1, -1, /* unused */
+ 16, 17, 18, 19, 20, 21, /* temp1..temp6 */
+ 30, 31 }; /* intrusion0, intrusion1 */
+
static const u16 NCT6776_REG_TOLERANCE_H[] = {
- 0x10c, 0x20c, 0x30c, 0x80c, 0x90c };
+ 0x10c, 0x20c, 0x30c, 0x80c, 0x90c, 0xa0c };
-static const u8 NCT6776_REG_PWM_MODE[] = { 0x04, 0, 0 };
-static const u8 NCT6776_PWM_MODE_MASK[] = { 0x01, 0, 0 };
+static const u8 NCT6776_REG_PWM_MODE[] = { 0x04, 0, 0, 0, 0, 0 };
+static const u8 NCT6776_PWM_MODE_MASK[] = { 0x01, 0, 0, 0, 0, 0 };
static const u16 NCT6776_REG_FAN_MIN[] = { 0x63a, 0x63c, 0x63e, 0x640, 0x642 };
static const u16 NCT6776_REG_FAN_PULSES[] = { 0x644, 0x645, 0x646, 0, 0 };
static const u16 NCT6776_REG_WEIGHT_DUTY_BASE[] = {
- 0x13e, 0x23e, 0x33e, 0x83e, 0x93e };
+ 0x13e, 0x23e, 0x33e, 0x83e, 0x93e, 0xa3e };
static const u16 NCT6776_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6775_REG_TEMP)] = {
0x18, 0x152, 0x252, 0x628, 0x629, 0x62A };
4, 5, 13, -1, -1, -1, /* temp1..temp6 */
12, 9 }; /* intrusion0, intrusion1 */
-static const u16 NCT6779_REG_FAN[] = { 0x4b0, 0x4b2, 0x4b4, 0x4b6, 0x4b8 };
+static const s8 NCT6779_BEEP_BITS[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, /* in0.. in7 */
+ 8, 9, 10, 11, 12, 13, 14, /* in8..in14 */
+ 24, /* global beep enable */
+ 25, 26, 27, 28, 29, /* fan1..fan5 */
+ -1, -1, -1, /* unused */
+ 16, 17, -1, -1, -1, -1, /* temp1..temp6 */
+ 30, 31 }; /* intrusion0, intrusion1 */
+
+static const u16 NCT6779_REG_FAN[] = {
+ 0x4b0, 0x4b2, 0x4b4, 0x4b6, 0x4b8, 0x4ba };
static const u16 NCT6779_REG_FAN_PULSES[] = {
- 0x644, 0x645, 0x646, 0x647, 0x648 };
+ 0x644, 0x645, 0x646, 0x647, 0x648, 0x649 };
static const u16 NCT6779_REG_CRITICAL_PWM_ENABLE[] = {
- 0x136, 0x236, 0x336, 0x836, 0x936 };
+ 0x136, 0x236, 0x336, 0x836, 0x936, 0xa36 };
+#define NCT6779_CRITICAL_PWM_ENABLE_MASK 0x01
static const u16 NCT6779_REG_CRITICAL_PWM[] = {
- 0x137, 0x237, 0x337, 0x837, 0x937 };
+ 0x137, 0x237, 0x337, 0x837, 0x937, 0xa37 };
static const u16 NCT6779_REG_TEMP[] = { 0x27, 0x150 };
static const u16 NCT6779_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6779_REG_TEMP)] = {
static const u16 NCT6779_REG_TEMP_CRIT[ARRAY_SIZE(nct6779_temp_label) - 1]
= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x709, 0x70a };
+/* NCT6791 specific data */
+
+#define NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE 0x28
+
+static const u16 NCT6791_REG_ALARM[NUM_REG_ALARM] = {
+ 0x459, 0x45A, 0x45B, 0x568, 0x45D };
+
+static const s8 NCT6791_ALARM_BITS[] = {
+ 0, 1, 2, 3, 8, 21, 20, 16, /* in0.. in7 */
+ 17, 24, 25, 26, 27, 28, 29, /* in8..in14 */
+ -1, /* unused */
+ 6, 7, 11, 10, 23, 33, /* fan1..fan6 */
+ -1, -1, /* unused */
+ 4, 5, 13, -1, -1, -1, /* temp1..temp6 */
+ 12, 9 }; /* intrusion0, intrusion1 */
+
+
+/* NCT6102D/NCT6106D specific data */
+
+#define NCT6106_REG_VBAT 0x318
+#define NCT6106_REG_DIODE 0x319
+#define NCT6106_DIODE_MASK 0x01
+
+static const u16 NCT6106_REG_IN_MAX[] = {
+ 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9e, 0xa0, 0xa2 };
+static const u16 NCT6106_REG_IN_MIN[] = {
+ 0x91, 0x93, 0x95, 0x97, 0x99, 0x9b, 0x9f, 0xa1, 0xa3 };
+static const u16 NCT6106_REG_IN[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x07, 0x08, 0x09 };
+
+static const u16 NCT6106_REG_TEMP[] = { 0x10, 0x11, 0x12, 0x13, 0x14, 0x15 };
+static const u16 NCT6106_REG_TEMP_HYST[] = {
+ 0xc3, 0xc7, 0xcb, 0xcf, 0xd3, 0xd7 };
+static const u16 NCT6106_REG_TEMP_OVER[] = {
+ 0xc2, 0xc6, 0xca, 0xce, 0xd2, 0xd6 };
+static const u16 NCT6106_REG_TEMP_CRIT_L[] = {
+ 0xc0, 0xc4, 0xc8, 0xcc, 0xd0, 0xd4 };
+static const u16 NCT6106_REG_TEMP_CRIT_H[] = {
+ 0xc1, 0xc5, 0xc9, 0xcf, 0xd1, 0xd5 };
+static const u16 NCT6106_REG_TEMP_OFFSET[] = { 0x311, 0x312, 0x313 };
+static const u16 NCT6106_REG_TEMP_CONFIG[] = {
+ 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc };
+
+static const u16 NCT6106_REG_FAN[] = { 0x20, 0x22, 0x24 };
+static const u16 NCT6106_REG_FAN_MIN[] = { 0xe0, 0xe2, 0xe4 };
+static const u16 NCT6106_REG_FAN_PULSES[] = { 0xf6, 0xf6, 0xf6, 0, 0 };
+static const u16 NCT6106_FAN_PULSE_SHIFT[] = { 0, 2, 4, 0, 0 };
+
+static const u8 NCT6106_REG_PWM_MODE[] = { 0xf3, 0xf3, 0xf3 };
+static const u8 NCT6106_PWM_MODE_MASK[] = { 0x01, 0x02, 0x04 };
+static const u16 NCT6106_REG_PWM[] = { 0x119, 0x129, 0x139 };
+static const u16 NCT6106_REG_PWM_READ[] = { 0x4a, 0x4b, 0x4c };
+static const u16 NCT6106_REG_FAN_MODE[] = { 0x113, 0x123, 0x133 };
+static const u16 NCT6106_REG_TEMP_SEL[] = { 0x110, 0x120, 0x130 };
+static const u16 NCT6106_REG_TEMP_SOURCE[] = {
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5 };
+
+static const u16 NCT6106_REG_CRITICAL_TEMP[] = { 0x11a, 0x12a, 0x13a };
+static const u16 NCT6106_REG_CRITICAL_TEMP_TOLERANCE[] = {
+ 0x11b, 0x12b, 0x13b };
+
+static const u16 NCT6106_REG_CRITICAL_PWM_ENABLE[] = { 0x11c, 0x12c, 0x13c };
+#define NCT6106_CRITICAL_PWM_ENABLE_MASK 0x10
+static const u16 NCT6106_REG_CRITICAL_PWM[] = { 0x11d, 0x12d, 0x13d };
+
+static const u16 NCT6106_REG_FAN_STEP_UP_TIME[] = { 0x114, 0x124, 0x134 };
+static const u16 NCT6106_REG_FAN_STEP_DOWN_TIME[] = { 0x115, 0x125, 0x135 };
+static const u16 NCT6106_REG_FAN_STOP_OUTPUT[] = { 0x116, 0x126, 0x136 };
+static const u16 NCT6106_REG_FAN_START_OUTPUT[] = { 0x117, 0x127, 0x137 };
+static const u16 NCT6106_REG_FAN_STOP_TIME[] = { 0x118, 0x128, 0x138 };
+static const u16 NCT6106_REG_TOLERANCE_H[] = { 0x112, 0x122, 0x132 };
+
+static const u16 NCT6106_REG_TARGET[] = { 0x111, 0x121, 0x131 };
+
+static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188 };
+static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189 };
+static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a };
+static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x17c };
+static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c };
+static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d };
+
+static const u16 NCT6106_REG_AUTO_TEMP[] = { 0x160, 0x170, 0x180 };
+static const u16 NCT6106_REG_AUTO_PWM[] = { 0x164, 0x174, 0x184 };
+
+static const u16 NCT6106_REG_ALARM[NUM_REG_ALARM] = {
+ 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d };
+
+static const s8 NCT6106_ALARM_BITS[] = {
+ 0, 1, 2, 3, 4, 5, 7, 8, /* in0.. in7 */
+ 9, -1, -1, -1, -1, -1, -1, /* in8..in14 */
+ -1, /* unused */
+ 32, 33, 34, -1, -1, /* fan1..fan5 */
+ -1, -1, -1, /* unused */
+ 16, 17, 18, 19, 20, 21, /* temp1..temp6 */
+ 48, -1 /* intrusion0, intrusion1 */
+};
+
+static const u16 NCT6106_REG_BEEP[NUM_REG_BEEP] = {
+ 0x3c0, 0x3c1, 0x3c2, 0x3c3, 0x3c4 };
+
+static const s8 NCT6106_BEEP_BITS[] = {
+ 0, 1, 2, 3, 4, 5, 7, 8, /* in0.. in7 */
+ 9, 10, 11, 12, -1, -1, -1, /* in8..in14 */
+ 32, /* global beep enable */
+ 24, 25, 26, 27, 28, /* fan1..fan5 */
+ -1, -1, -1, /* unused */
+ 16, 17, 18, 19, 20, 21, /* temp1..temp6 */
+ 34, -1 /* intrusion0, intrusion1 */
+};
+
+static const u16 NCT6106_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6776_temp_label) - 1]
+ = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x51, 0x52, 0x54 };
+
+static const u16 NCT6106_REG_TEMP_CRIT[ARRAY_SIZE(nct6776_temp_label) - 1]
+ = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x204, 0x205 };
+
static enum pwm_enable reg_to_pwm_enable(int pwm, int mode)
{
if (mode == 0 && pwm == 255)
struct nct6775_data {
int addr; /* IO base of hw monitor block */
+ int sioreg; /* SIO register address */
enum kinds kind;
const char *name;
struct device *hwmon_dev;
+ struct attribute_group *group_in;
+ struct attribute_group *group_fan;
+ struct attribute_group *group_temp;
+ struct attribute_group *group_pwm;
- u16 reg_temp[4][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst,
- * 3=temp_crit
+ u16 reg_temp[5][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst,
+ * 3=temp_crit, 4=temp_lcrit
*/
u8 temp_src[NUM_TEMP];
u16 reg_temp_config[NUM_TEMP];
u16 REG_CONFIG;
u16 REG_VBAT;
u16 REG_DIODE;
+ u8 DIODE_MASK;
const s8 *ALARM_BITS;
+ const s8 *BEEP_BITS;
const u16 *REG_VIN;
const u16 *REG_IN_MINMAX[2];
const u16 *REG_FAN_MODE;
const u16 *REG_FAN_MIN;
const u16 *REG_FAN_PULSES;
+ const u16 *FAN_PULSE_SHIFT;
const u16 *REG_FAN_TIME[3];
const u16 *REG_TOLERANCE_H;
*/
const u16 *REG_PWM_READ;
+ const u16 *REG_CRITICAL_PWM_ENABLE;
+ u8 CRITICAL_PWM_ENABLE_MASK;
+ const u16 *REG_CRITICAL_PWM;
+
const u16 *REG_AUTO_TEMP;
const u16 *REG_AUTO_PWM;
const u16 *REG_TEMP_OFFSET;
const u16 *REG_ALARM;
+ const u16 *REG_BEEP;
unsigned int (*fan_from_reg)(u16 reg, unsigned int divreg);
unsigned int (*fan_from_reg_min)(u16 reg, unsigned int divreg);
u8 bank; /* current register bank */
u8 in_num; /* number of in inputs we have */
u8 in[15][3]; /* [0]=in, [1]=in_max, [2]=in_min */
- unsigned int rpm[5];
- u16 fan_min[5];
- u8 fan_pulses[5];
- u8 fan_div[5];
+ unsigned int rpm[NUM_FAN];
+ u16 fan_min[NUM_FAN];
+ u8 fan_pulses[NUM_FAN];
+ u8 fan_div[NUM_FAN];
u8 has_pwm;
u8 has_fan; /* some fan inputs can be disabled */
u8 has_fan_min; /* some fans don't have min register */
bool has_fan_div;
- u8 num_temp_alarms; /* 2 or 3 */
+ u8 num_temp_alarms; /* 2, 3, or 6 */
+ u8 num_temp_beeps; /* 2, 3, or 6 */
u8 temp_fixed_num; /* 3 or 6 */
u8 temp_type[NUM_TEMP_FIXED];
s8 temp_offset[NUM_TEMP_FIXED];
- s16 temp[4][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst,
- * 3=temp_crit */
+ s16 temp[5][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst,
+ * 3=temp_crit, 4=temp_lcrit */
u64 alarms;
+ u64 beeps;
u8 pwm_num; /* number of pwm */
- u8 pwm_mode[5]; /* 1->DC variable voltage, 0->PWM variable duty cycle */
- enum pwm_enable pwm_enable[5];
+ u8 pwm_mode[NUM_FAN]; /* 1->DC variable voltage,
+ * 0->PWM variable duty cycle
+ */
+ enum pwm_enable pwm_enable[NUM_FAN];
/* 0->off
* 1->manual
* 2->thermal cruise mode (also called SmartFan I)
* 4->SmartFan III
* 5->enhanced variable thermal cruise (SmartFan IV)
*/
- u8 pwm[7][5]; /* [0]=pwm, [1]=pwm_start, [2]=pwm_floor,
- * [3]=pwm_max, [4]=pwm_step,
- * [5]=weight_duty_step, [6]=weight_duty_base
- */
+ u8 pwm[7][NUM_FAN]; /* [0]=pwm, [1]=pwm_start, [2]=pwm_floor,
+ * [3]=pwm_max, [4]=pwm_step,
+ * [5]=weight_duty_step, [6]=weight_duty_base
+ */
- u8 target_temp[5];
+ u8 target_temp[NUM_FAN];
u8 target_temp_mask;
- u32 target_speed[5];
- u32 target_speed_tolerance[5];
+ u32 target_speed[NUM_FAN];
+ u32 target_speed_tolerance[NUM_FAN];
u8 speed_tolerance_limit;
- u8 temp_tolerance[2][5];
+ u8 temp_tolerance[2][NUM_FAN];
u8 tolerance_mask;
- u8 fan_time[3][5]; /* 0 = stop_time, 1 = step_up, 2 = step_down */
+ u8 fan_time[3][NUM_FAN]; /* 0 = stop_time, 1 = step_up, 2 = step_down */
/* Automatic fan speed control registers */
int auto_pwm_num;
- u8 auto_pwm[5][7];
- u8 auto_temp[5][7];
- u8 pwm_temp_sel[5];
- u8 pwm_weight_temp_sel[5];
- u8 weight_temp[3][5]; /* 0->temp_step, 1->temp_step_tol,
- * 2->temp_base
- */
+ u8 auto_pwm[NUM_FAN][7];
+ u8 auto_temp[NUM_FAN][7];
+ u8 pwm_temp_sel[NUM_FAN];
+ u8 pwm_weight_temp_sel[NUM_FAN];
+ u8 weight_temp[3][NUM_FAN]; /* 0->temp_step, 1->temp_step_tol,
+ * 2->temp_base
+ */
u8 vid;
u8 vrm;
+ bool have_vid;
+
u16 have_temp;
u16 have_temp_fixed;
u16 have_in;
enum kinds kind;
};
+struct sensor_device_template {
+ struct device_attribute dev_attr;
+ union {
+ struct {
+ u8 nr;
+ u8 index;
+ } s;
+ int index;
+ } u;
+ bool s2; /* true if both index and nr are used */
+};
+
+struct sensor_device_attr_u {
+ union {
+ struct sensor_device_attribute a1;
+ struct sensor_device_attribute_2 a2;
+ } u;
+ char name[32];
+};
+
+#define __TEMPLATE_ATTR(_template, _mode, _show, _store) { \
+ .attr = {.name = _template, .mode = _mode }, \
+ .show = _show, \
+ .store = _store, \
+}
+
+#define SENSOR_DEVICE_TEMPLATE(_template, _mode, _show, _store, _index) \
+ { .dev_attr = __TEMPLATE_ATTR(_template, _mode, _show, _store), \
+ .u.index = _index, \
+ .s2 = false }
+
+#define SENSOR_DEVICE_TEMPLATE_2(_template, _mode, _show, _store, \
+ _nr, _index) \
+ { .dev_attr = __TEMPLATE_ATTR(_template, _mode, _show, _store), \
+ .u.s.index = _index, \
+ .u.s.nr = _nr, \
+ .s2 = true }
+
+#define SENSOR_TEMPLATE(_name, _template, _mode, _show, _store, _index) \
+static struct sensor_device_template sensor_dev_template_##_name \
+ = SENSOR_DEVICE_TEMPLATE(_template, _mode, _show, _store, \
+ _index)
+
+#define SENSOR_TEMPLATE_2(_name, _template, _mode, _show, _store, \
+ _nr, _index) \
+static struct sensor_device_template sensor_dev_template_##_name \
+ = SENSOR_DEVICE_TEMPLATE_2(_template, _mode, _show, _store, \
+ _nr, _index)
+
+struct sensor_template_group {
+ struct sensor_device_template **templates;
+ umode_t (*is_visible)(struct kobject *, struct attribute *, int);
+ int base;
+};
+
+static struct attribute_group *
+nct6775_create_attr_group(struct device *dev, struct sensor_template_group *tg,
+ int repeat)
+{
+ struct attribute_group *group;
+ struct sensor_device_attr_u *su;
+ struct sensor_device_attribute *a;
+ struct sensor_device_attribute_2 *a2;
+ struct attribute **attrs;
+ struct sensor_device_template **t;
+ int err, i, j, count;
+
+ if (repeat <= 0)
+ return ERR_PTR(-EINVAL);
+
+ t = tg->templates;
+ for (count = 0; *t; t++, count++)
+ ;
+
+ if (count == 0)
+ return ERR_PTR(-EINVAL);
+
+ group = devm_kzalloc(dev, sizeof(*group), GFP_KERNEL);
+ if (group == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ attrs = devm_kzalloc(dev, sizeof(*attrs) * (repeat * count + 1),
+ GFP_KERNEL);
+ if (attrs == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ su = devm_kzalloc(dev, sizeof(*su) * repeat * count,
+ GFP_KERNEL);
+ if (su == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ group->attrs = attrs;
+ group->is_visible = tg->is_visible;
+
+ for (i = 0; i < repeat; i++) {
+ t = tg->templates;
+ for (j = 0; *t != NULL; j++) {
+ snprintf(su->name, sizeof(su->name),
+ (*t)->dev_attr.attr.name, tg->base + i);
+ if ((*t)->s2) {
+ a2 = &su->u.a2;
+ a2->dev_attr.attr.name = su->name;
+ a2->nr = (*t)->u.s.nr + i;
+ a2->index = (*t)->u.s.index;
+ a2->dev_attr.attr.mode =
+ (*t)->dev_attr.attr.mode;
+ a2->dev_attr.show = (*t)->dev_attr.show;
+ a2->dev_attr.store = (*t)->dev_attr.store;
+ *attrs = &a2->dev_attr.attr;
+ } else {
+ a = &su->u.a1;
+ a->dev_attr.attr.name = su->name;
+ a->index = (*t)->u.index + i;
+ a->dev_attr.attr.mode =
+ (*t)->dev_attr.attr.mode;
+ a->dev_attr.show = (*t)->dev_attr.show;
+ a->dev_attr.store = (*t)->dev_attr.store;
+ *attrs = &a->dev_attr.attr;
+ }
+ attrs++;
+ su++;
+ t++;
+ }
+ }
+
+ err = sysfs_create_group(&dev->kobj, group);
+ if (err)
+ return ERR_PTR(-ENOMEM);
+
+ return group;
+}
+
static bool is_word_sized(struct nct6775_data *data, u16 reg)
{
switch (data->kind) {
+ case nct6106:
+ return reg == 0x20 || reg == 0x22 || reg == 0x24 ||
+ reg == 0xe0 || reg == 0xe2 || reg == 0xe4 ||
+ reg == 0x111 || reg == 0x121 || reg == 0x131;
case nct6775:
return (((reg & 0xff00) == 0x100 ||
(reg & 0xff00) == 0x200) &&
((reg & 0xfff0) == 0x650 && (reg & 0x000f) >= 0x06) ||
reg == 0x73 || reg == 0x75 || reg == 0x77;
case nct6779:
+ case nct6791:
return reg == 0x150 || reg == 0x153 || reg == 0x155 ||
- ((reg & 0xfff0) == 0x4b0 && (reg & 0x000f) < 0x09) ||
+ ((reg & 0xfff0) == 0x4b0 && (reg & 0x000f) < 0x0b) ||
reg == 0x402 ||
reg == 0x63a || reg == 0x63c || reg == 0x63e ||
reg == 0x640 || reg == 0x642 ||
case nct6776:
data->auto_pwm[i][data->auto_pwm_num] = 0xff;
break;
+ case nct6106:
case nct6779:
+ case nct6791:
reg = nct6775_read_value(data,
- NCT6779_REG_CRITICAL_PWM_ENABLE[i]);
- if (reg & 1)
- data->auto_pwm[i][data->auto_pwm_num] =
- nct6775_read_value(data,
- NCT6779_REG_CRITICAL_PWM[i]);
+ data->REG_CRITICAL_PWM_ENABLE[i]);
+ if (reg & data->CRITICAL_PWM_ENABLE_MASK)
+ reg = nct6775_read_value(data,
+ data->REG_CRITICAL_PWM[i]);
else
- data->auto_pwm[i][data->auto_pwm_num] = 0xff;
+ reg = 0xff;
+ data->auto_pwm[i][data->auto_pwm_num] = reg;
break;
}
}
data->fan_min[i] = nct6775_read_value(data,
data->REG_FAN_MIN[i]);
data->fan_pulses[i] =
- nct6775_read_value(data, data->REG_FAN_PULSES[i]);
+ (nct6775_read_value(data, data->REG_FAN_PULSES[i])
+ >> data->FAN_PULSE_SHIFT[i]) & 0x03;
nct6775_select_fan_div(dev, data, i, reg);
}
data->alarms |= ((u64)alarm) << (i << 3);
}
+ 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]);
+ data->beeps |= ((u64)beep) << (i << 3);
+ }
+
data->last_updated = jiffies;
data->valid = true;
}
return sprintf(buf, "%u\n", alarm);
}
-static SENSOR_DEVICE_ATTR_2(in0_input, S_IRUGO, show_in_reg, NULL, 0, 0);
-static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, show_in_reg, NULL, 1, 0);
-static SENSOR_DEVICE_ATTR_2(in2_input, S_IRUGO, show_in_reg, NULL, 2, 0);
-static SENSOR_DEVICE_ATTR_2(in3_input, S_IRUGO, show_in_reg, NULL, 3, 0);
-static SENSOR_DEVICE_ATTR_2(in4_input, S_IRUGO, show_in_reg, NULL, 4, 0);
-static SENSOR_DEVICE_ATTR_2(in5_input, S_IRUGO, show_in_reg, NULL, 5, 0);
-static SENSOR_DEVICE_ATTR_2(in6_input, S_IRUGO, show_in_reg, NULL, 6, 0);
-static SENSOR_DEVICE_ATTR_2(in7_input, S_IRUGO, show_in_reg, NULL, 7, 0);
-static SENSOR_DEVICE_ATTR_2(in8_input, S_IRUGO, show_in_reg, NULL, 8, 0);
-static SENSOR_DEVICE_ATTR_2(in9_input, S_IRUGO, show_in_reg, NULL, 9, 0);
-static SENSOR_DEVICE_ATTR_2(in10_input, S_IRUGO, show_in_reg, NULL, 10, 0);
-static SENSOR_DEVICE_ATTR_2(in11_input, S_IRUGO, show_in_reg, NULL, 11, 0);
-static SENSOR_DEVICE_ATTR_2(in12_input, S_IRUGO, show_in_reg, NULL, 12, 0);
-static SENSOR_DEVICE_ATTR_2(in13_input, S_IRUGO, show_in_reg, NULL, 13, 0);
-static SENSOR_DEVICE_ATTR_2(in14_input, S_IRUGO, show_in_reg, NULL, 14, 0);
-
-static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0);
-static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1);
-static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2);
-static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3);
-static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 4);
-static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 5);
-static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 6);
-static SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 7);
-static SENSOR_DEVICE_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 8);
-static SENSOR_DEVICE_ATTR(in9_alarm, S_IRUGO, show_alarm, NULL, 9);
-static SENSOR_DEVICE_ATTR(in10_alarm, S_IRUGO, show_alarm, NULL, 10);
-static SENSOR_DEVICE_ATTR(in11_alarm, S_IRUGO, show_alarm, NULL, 11);
-static SENSOR_DEVICE_ATTR(in12_alarm, S_IRUGO, show_alarm, NULL, 12);
-static SENSOR_DEVICE_ATTR(in13_alarm, S_IRUGO, show_alarm, NULL, 13);
-static SENSOR_DEVICE_ATTR(in14_alarm, S_IRUGO, show_alarm, NULL, 14);
-
-static SENSOR_DEVICE_ATTR_2(in0_min, S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 0, 1);
-static SENSOR_DEVICE_ATTR_2(in1_min, S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 1, 1);
-static SENSOR_DEVICE_ATTR_2(in2_min, S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 2, 1);
-static SENSOR_DEVICE_ATTR_2(in3_min, S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 3, 1);
-static SENSOR_DEVICE_ATTR_2(in4_min, S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 4, 1);
-static SENSOR_DEVICE_ATTR_2(in5_min, S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 5, 1);
-static SENSOR_DEVICE_ATTR_2(in6_min, S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 6, 1);
-static SENSOR_DEVICE_ATTR_2(in7_min, S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 7, 1);
-static SENSOR_DEVICE_ATTR_2(in8_min, S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 8, 1);
-static SENSOR_DEVICE_ATTR_2(in9_min, S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 9, 1);
-static SENSOR_DEVICE_ATTR_2(in10_min, S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 10, 1);
-static SENSOR_DEVICE_ATTR_2(in11_min, S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 11, 1);
-static SENSOR_DEVICE_ATTR_2(in12_min, S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 12, 1);
-static SENSOR_DEVICE_ATTR_2(in13_min, S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 13, 1);
-static SENSOR_DEVICE_ATTR_2(in14_min, S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 14, 1);
-
-static SENSOR_DEVICE_ATTR_2(in0_max, S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 0, 2);
-static SENSOR_DEVICE_ATTR_2(in1_max, S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 1, 2);
-static SENSOR_DEVICE_ATTR_2(in2_max, S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 2, 2);
-static SENSOR_DEVICE_ATTR_2(in3_max, S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 3, 2);
-static SENSOR_DEVICE_ATTR_2(in4_max, S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 4, 2);
-static SENSOR_DEVICE_ATTR_2(in5_max, S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 5, 2);
-static SENSOR_DEVICE_ATTR_2(in6_max, S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 6, 2);
-static SENSOR_DEVICE_ATTR_2(in7_max, S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 7, 2);
-static SENSOR_DEVICE_ATTR_2(in8_max, S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 8, 2);
-static SENSOR_DEVICE_ATTR_2(in9_max, S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 9, 2);
-static SENSOR_DEVICE_ATTR_2(in10_max, S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 10, 2);
-static SENSOR_DEVICE_ATTR_2(in11_max, S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 11, 2);
-static SENSOR_DEVICE_ATTR_2(in12_max, S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 12, 2);
-static SENSOR_DEVICE_ATTR_2(in13_max, S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 13, 2);
-static SENSOR_DEVICE_ATTR_2(in14_max, S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 14, 2);
-
-static struct attribute *nct6775_attributes_in[15][5] = {
- {
- &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,
- NULL
- },
- {
- &sensor_dev_attr_in1_input.dev_attr.attr,
- &sensor_dev_attr_in1_min.dev_attr.attr,
- &sensor_dev_attr_in1_max.dev_attr.attr,
- &sensor_dev_attr_in1_alarm.dev_attr.attr,
- NULL
- },
- {
- &sensor_dev_attr_in2_input.dev_attr.attr,
- &sensor_dev_attr_in2_min.dev_attr.attr,
- &sensor_dev_attr_in2_max.dev_attr.attr,
- &sensor_dev_attr_in2_alarm.dev_attr.attr,
- NULL
- },
- {
- &sensor_dev_attr_in3_input.dev_attr.attr,
- &sensor_dev_attr_in3_min.dev_attr.attr,
- &sensor_dev_attr_in3_max.dev_attr.attr,
- &sensor_dev_attr_in3_alarm.dev_attr.attr,
- NULL
- },
- {
- &sensor_dev_attr_in4_input.dev_attr.attr,
- &sensor_dev_attr_in4_min.dev_attr.attr,
- &sensor_dev_attr_in4_max.dev_attr.attr,
- &sensor_dev_attr_in4_alarm.dev_attr.attr,
- NULL
- },
- {
- &sensor_dev_attr_in5_input.dev_attr.attr,
- &sensor_dev_attr_in5_min.dev_attr.attr,
- &sensor_dev_attr_in5_max.dev_attr.attr,
- &sensor_dev_attr_in5_alarm.dev_attr.attr,
- NULL
- },
- {
- &sensor_dev_attr_in6_input.dev_attr.attr,
- &sensor_dev_attr_in6_min.dev_attr.attr,
- &sensor_dev_attr_in6_max.dev_attr.attr,
- &sensor_dev_attr_in6_alarm.dev_attr.attr,
- NULL
- },
- {
- &sensor_dev_attr_in7_input.dev_attr.attr,
- &sensor_dev_attr_in7_min.dev_attr.attr,
- &sensor_dev_attr_in7_max.dev_attr.attr,
- &sensor_dev_attr_in7_alarm.dev_attr.attr,
- NULL
- },
- {
- &sensor_dev_attr_in8_input.dev_attr.attr,
- &sensor_dev_attr_in8_min.dev_attr.attr,
- &sensor_dev_attr_in8_max.dev_attr.attr,
- &sensor_dev_attr_in8_alarm.dev_attr.attr,
- NULL
- },
- {
- &sensor_dev_attr_in9_input.dev_attr.attr,
- &sensor_dev_attr_in9_min.dev_attr.attr,
- &sensor_dev_attr_in9_max.dev_attr.attr,
- &sensor_dev_attr_in9_alarm.dev_attr.attr,
- NULL
- },
- {
- &sensor_dev_attr_in10_input.dev_attr.attr,
- &sensor_dev_attr_in10_min.dev_attr.attr,
- &sensor_dev_attr_in10_max.dev_attr.attr,
- &sensor_dev_attr_in10_alarm.dev_attr.attr,
- NULL
- },
- {
- &sensor_dev_attr_in11_input.dev_attr.attr,
- &sensor_dev_attr_in11_min.dev_attr.attr,
- &sensor_dev_attr_in11_max.dev_attr.attr,
- &sensor_dev_attr_in11_alarm.dev_attr.attr,
- NULL
- },
- {
- &sensor_dev_attr_in12_input.dev_attr.attr,
- &sensor_dev_attr_in12_min.dev_attr.attr,
- &sensor_dev_attr_in12_max.dev_attr.attr,
- &sensor_dev_attr_in12_alarm.dev_attr.attr,
- NULL
- },
- {
- &sensor_dev_attr_in13_input.dev_attr.attr,
- &sensor_dev_attr_in13_min.dev_attr.attr,
- &sensor_dev_attr_in13_max.dev_attr.attr,
- &sensor_dev_attr_in13_alarm.dev_attr.attr,
- NULL
- },
- {
- &sensor_dev_attr_in14_input.dev_attr.attr,
- &sensor_dev_attr_in14_min.dev_attr.attr,
- &sensor_dev_attr_in14_max.dev_attr.attr,
- &sensor_dev_attr_in14_alarm.dev_attr.attr,
- NULL
- },
+static ssize_t
+show_beep(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ struct nct6775_data *data = nct6775_update_device(dev);
+ int nr = data->BEEP_BITS[sattr->index];
+
+ return sprintf(buf, "%u\n",
+ (unsigned int)((data->beeps >> nr) & 0x01));
+}
+
+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 nct6775_data *data = dev_get_drvdata(dev);
+ int nr = data->BEEP_BITS[sattr->index];
+ int regindex = nr >> 3;
+ unsigned long val;
+
+ int err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+ if (val > 1)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ if (val)
+ data->beeps |= (1ULL << nr);
+ else
+ data->beeps &= ~(1ULL << nr);
+ nct6775_write_value(data, data->REG_BEEP[regindex],
+ (data->beeps >> (regindex << 3)) & 0xff);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t
+show_temp_beep(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ struct nct6775_data *data = nct6775_update_device(dev);
+ unsigned int beep = 0;
+ int nr;
+
+ /*
+ * For temperatures, there is no fixed mapping from registers to beep
+ * enable bits. Beep enable bits are determined by the temperature
+ * source mapping.
+ */
+ 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);
+}
+
+static ssize_t
+store_temp_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 nct6775_data *data = dev_get_drvdata(dev);
+ int nr, bit, regindex;
+ unsigned long val;
+
+ int err = kstrtoul(buf, 10, &val);
+ if (err < 0)
+ return err;
+ if (val > 1)
+ return -EINVAL;
+
+ nr = find_temp_source(data, sattr->index, data->num_temp_beeps);
+ if (nr < 0)
+ return -ENODEV;
+
+ bit = data->BEEP_BITS[nr + TEMP_ALARM_BASE];
+ regindex = bit >> 3;
+
+ mutex_lock(&data->update_lock);
+ if (val)
+ data->beeps |= (1ULL << bit);
+ else
+ data->beeps &= ~(1ULL << bit);
+ nct6775_write_value(data, data->REG_BEEP[regindex],
+ (data->beeps >> (regindex << 3)) & 0xff);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+static umode_t nct6775_in_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ int in = index / 5; /* voltage index */
+
+ if (!(data->have_in & (1 << in)))
+ return 0;
+
+ return attr->mode;
+}
+
+SENSOR_TEMPLATE_2(in_input, "in%d_input", S_IRUGO, show_in_reg, NULL, 0, 0);
+SENSOR_TEMPLATE(in_alarm, "in%d_alarm", S_IRUGO, show_alarm, NULL, 0);
+SENSOR_TEMPLATE(in_beep, "in%d_beep", S_IWUSR | S_IRUGO, show_beep, store_beep,
+ 0);
+SENSOR_TEMPLATE_2(in_min, "in%d_min", S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 0, 1);
+SENSOR_TEMPLATE_2(in_max, "in%d_max", S_IWUSR | S_IRUGO, show_in_reg,
+ store_in_reg, 0, 2);
+
+/*
+ * nct6775_in_is_visible uses the index into the following array
+ * to determine if attributes should be created or not.
+ * Any change in order or content must be matched.
+ */
+static struct sensor_device_template *nct6775_attributes_in_template[] = {
+ &sensor_dev_template_in_input,
+ &sensor_dev_template_in_alarm,
+ &sensor_dev_template_in_beep,
+ &sensor_dev_template_in_min,
+ &sensor_dev_template_in_max,
+ NULL
};
-static const struct attribute_group nct6775_group_in[15] = {
- { .attrs = nct6775_attributes_in[0] },
- { .attrs = nct6775_attributes_in[1] },
- { .attrs = nct6775_attributes_in[2] },
- { .attrs = nct6775_attributes_in[3] },
- { .attrs = nct6775_attributes_in[4] },
- { .attrs = nct6775_attributes_in[5] },
- { .attrs = nct6775_attributes_in[6] },
- { .attrs = nct6775_attributes_in[7] },
- { .attrs = nct6775_attributes_in[8] },
- { .attrs = nct6775_attributes_in[9] },
- { .attrs = nct6775_attributes_in[10] },
- { .attrs = nct6775_attributes_in[11] },
- { .attrs = nct6775_attributes_in[12] },
- { .attrs = nct6775_attributes_in[13] },
- { .attrs = nct6775_attributes_in[14] },
+static struct sensor_template_group nct6775_in_template_group = {
+ .templates = nct6775_attributes_in_template,
+ .is_visible = nct6775_in_is_visible,
};
static ssize_t
int nr = sattr->index;
unsigned long val;
int err;
+ u8 reg;
err = kstrtoul(buf, 10, &val);
if (err < 0)
mutex_lock(&data->update_lock);
data->fan_pulses[nr] = val & 3;
- nct6775_write_value(data, data->REG_FAN_PULSES[nr], val & 3);
+ reg = nct6775_read_value(data, data->REG_FAN_PULSES[nr]);
+ reg &= ~(0x03 << data->FAN_PULSE_SHIFT[nr]);
+ reg |= (val & 3) << data->FAN_PULSE_SHIFT[nr];
+ nct6775_write_value(data, data->REG_FAN_PULSES[nr], reg);
mutex_unlock(&data->update_lock);
return count;
}
-static struct sensor_device_attribute sda_fan_input[] = {
- SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0),
- SENSOR_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1),
- SENSOR_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2),
- SENSOR_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3),
- SENSOR_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 4),
-};
+static umode_t nct6775_fan_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ int fan = index / 6; /* fan index */
+ int nr = index % 6; /* attribute index */
-static struct sensor_device_attribute sda_fan_alarm[] = {
- SENSOR_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE),
- SENSOR_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE + 1),
- SENSOR_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE + 2),
- SENSOR_ATTR(fan4_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE + 3),
- SENSOR_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE + 4),
-};
+ if (!(data->has_fan & (1 << fan)))
+ return 0;
-static struct sensor_device_attribute sda_fan_min[] = {
- SENSOR_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min,
- store_fan_min, 0),
- SENSOR_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min,
- store_fan_min, 1),
- SENSOR_ATTR(fan3_min, S_IWUSR | S_IRUGO, show_fan_min,
- store_fan_min, 2),
- SENSOR_ATTR(fan4_min, S_IWUSR | S_IRUGO, show_fan_min,
- store_fan_min, 3),
- SENSOR_ATTR(fan5_min, S_IWUSR | S_IRUGO, show_fan_min,
- store_fan_min, 4),
-};
+ if (nr == 1 && data->ALARM_BITS[FAN_ALARM_BASE + fan] == -1)
+ return 0;
+ if (nr == 2 && data->BEEP_BITS[FAN_ALARM_BASE + fan] == -1)
+ return 0;
+ if (nr == 4 && !(data->has_fan_min & (1 << fan)))
+ return 0;
+ if (nr == 5 && data->kind != nct6775)
+ return 0;
+
+ return attr->mode;
+}
-static struct sensor_device_attribute sda_fan_pulses[] = {
- SENSOR_ATTR(fan1_pulses, S_IWUSR | S_IRUGO, show_fan_pulses,
- store_fan_pulses, 0),
- SENSOR_ATTR(fan2_pulses, S_IWUSR | S_IRUGO, show_fan_pulses,
- store_fan_pulses, 1),
- SENSOR_ATTR(fan3_pulses, S_IWUSR | S_IRUGO, show_fan_pulses,
- store_fan_pulses, 2),
- SENSOR_ATTR(fan4_pulses, S_IWUSR | S_IRUGO, show_fan_pulses,
- store_fan_pulses, 3),
- SENSOR_ATTR(fan5_pulses, S_IWUSR | S_IRUGO, show_fan_pulses,
- store_fan_pulses, 4),
+SENSOR_TEMPLATE(fan_input, "fan%d_input", S_IRUGO, show_fan, NULL, 0);
+SENSOR_TEMPLATE(fan_alarm, "fan%d_alarm", S_IRUGO, show_alarm, NULL,
+ FAN_ALARM_BASE);
+SENSOR_TEMPLATE(fan_beep, "fan%d_beep", S_IWUSR | S_IRUGO, show_beep,
+ store_beep, FAN_ALARM_BASE);
+SENSOR_TEMPLATE(fan_pulses, "fan%d_pulses", S_IWUSR | S_IRUGO, show_fan_pulses,
+ store_fan_pulses, 0);
+SENSOR_TEMPLATE(fan_min, "fan%d_min", S_IWUSR | S_IRUGO, show_fan_min,
+ store_fan_min, 0);
+SENSOR_TEMPLATE(fan_div, "fan%d_div", S_IRUGO, show_fan_div, NULL, 0);
+
+/*
+ * nct6775_fan_is_visible uses the index into the following array
+ * to determine if attributes should be created or not.
+ * Any change in order or content must be matched.
+ */
+static struct sensor_device_template *nct6775_attributes_fan_template[] = {
+ &sensor_dev_template_fan_input,
+ &sensor_dev_template_fan_alarm, /* 1 */
+ &sensor_dev_template_fan_beep, /* 2 */
+ &sensor_dev_template_fan_pulses,
+ &sensor_dev_template_fan_min, /* 4 */
+ &sensor_dev_template_fan_div, /* 5 */
+ NULL
};
-static struct sensor_device_attribute sda_fan_div[] = {
- SENSOR_ATTR(fan1_div, S_IRUGO, show_fan_div, NULL, 0),
- SENSOR_ATTR(fan2_div, S_IRUGO, show_fan_div, NULL, 1),
- SENSOR_ATTR(fan3_div, S_IRUGO, show_fan_div, NULL, 2),
- SENSOR_ATTR(fan4_div, S_IRUGO, show_fan_div, NULL, 3),
- SENSOR_ATTR(fan5_div, S_IRUGO, show_fan_div, NULL, 4),
+static struct sensor_template_group nct6775_fan_template_group = {
+ .templates = nct6775_attributes_fan_template,
+ .is_visible = nct6775_fan_is_visible,
+ .base = 1,
};
static ssize_t
int nr = sattr->index;
unsigned long val;
int err;
- u8 vbat, diode, bit;
+ u8 vbat, diode, vbit, dbit;
err = kstrtoul(buf, 10, &val);
if (err < 0)
mutex_lock(&data->update_lock);
data->temp_type[nr] = val;
- vbat = nct6775_read_value(data, data->REG_VBAT) & ~(0x02 << nr);
- diode = nct6775_read_value(data, data->REG_DIODE) & ~(0x02 << nr);
- bit = 0x02 << nr;
+ vbit = 0x02 << nr;
+ dbit = data->DIODE_MASK << nr;
+ vbat = nct6775_read_value(data, data->REG_VBAT) & ~vbit;
+ diode = nct6775_read_value(data, data->REG_DIODE) & ~dbit;
switch (val) {
case 1: /* CPU diode (diode, current mode) */
- vbat |= bit;
- diode |= bit;
+ vbat |= vbit;
+ diode |= dbit;
break;
case 3: /* diode, voltage mode */
- vbat |= bit;
+ vbat |= dbit;
break;
case 4: /* thermistor */
break;
return count;
}
-static struct sensor_device_attribute_2 sda_temp_input[] = {
- SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0),
- SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 1, 0),
- SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 2, 0),
- SENSOR_ATTR_2(temp4_input, S_IRUGO, show_temp, NULL, 3, 0),
- SENSOR_ATTR_2(temp5_input, S_IRUGO, show_temp, NULL, 4, 0),
- SENSOR_ATTR_2(temp6_input, S_IRUGO, show_temp, NULL, 5, 0),
- SENSOR_ATTR_2(temp7_input, S_IRUGO, show_temp, NULL, 6, 0),
- SENSOR_ATTR_2(temp8_input, S_IRUGO, show_temp, NULL, 7, 0),
- SENSOR_ATTR_2(temp9_input, S_IRUGO, show_temp, NULL, 8, 0),
- SENSOR_ATTR_2(temp10_input, S_IRUGO, show_temp, NULL, 9, 0),
-};
+static umode_t nct6775_temp_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ int temp = index / 10; /* temp index */
+ int nr = index % 10; /* attribute index */
-static struct sensor_device_attribute sda_temp_label[] = {
- SENSOR_ATTR(temp1_label, S_IRUGO, show_temp_label, NULL, 0),
- SENSOR_ATTR(temp2_label, S_IRUGO, show_temp_label, NULL, 1),
- SENSOR_ATTR(temp3_label, S_IRUGO, show_temp_label, NULL, 2),
- SENSOR_ATTR(temp4_label, S_IRUGO, show_temp_label, NULL, 3),
- SENSOR_ATTR(temp5_label, S_IRUGO, show_temp_label, NULL, 4),
- SENSOR_ATTR(temp6_label, S_IRUGO, show_temp_label, NULL, 5),
- SENSOR_ATTR(temp7_label, S_IRUGO, show_temp_label, NULL, 6),
- SENSOR_ATTR(temp8_label, S_IRUGO, show_temp_label, NULL, 7),
- SENSOR_ATTR(temp9_label, S_IRUGO, show_temp_label, NULL, 8),
- SENSOR_ATTR(temp10_label, S_IRUGO, show_temp_label, NULL, 9),
-};
+ if (!(data->have_temp & (1 << temp)))
+ return 0;
-static struct sensor_device_attribute_2 sda_temp_max[] = {
- SENSOR_ATTR_2(temp1_max, S_IRUGO | S_IWUSR, show_temp, store_temp,
- 0, 1),
- SENSOR_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp, store_temp,
- 1, 1),
- SENSOR_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, show_temp, store_temp,
- 2, 1),
- SENSOR_ATTR_2(temp4_max, S_IRUGO | S_IWUSR, show_temp, store_temp,
- 3, 1),
- SENSOR_ATTR_2(temp5_max, S_IRUGO | S_IWUSR, show_temp, store_temp,
- 4, 1),
- SENSOR_ATTR_2(temp6_max, S_IRUGO | S_IWUSR, show_temp, store_temp,
- 5, 1),
- SENSOR_ATTR_2(temp7_max, S_IRUGO | S_IWUSR, show_temp, store_temp,
- 6, 1),
- SENSOR_ATTR_2(temp8_max, S_IRUGO | S_IWUSR, show_temp, store_temp,
- 7, 1),
- SENSOR_ATTR_2(temp9_max, S_IRUGO | S_IWUSR, show_temp, store_temp,
- 8, 1),
- SENSOR_ATTR_2(temp10_max, S_IRUGO | S_IWUSR, show_temp, store_temp,
- 9, 1),
-};
+ if (nr == 2 && find_temp_source(data, temp, data->num_temp_alarms) < 0)
+ return 0; /* alarm */
-static struct sensor_device_attribute_2 sda_temp_max_hyst[] = {
- SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp,
- 0, 2),
- SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp,
- 1, 2),
- SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp,
- 2, 2),
- SENSOR_ATTR_2(temp4_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp,
- 3, 2),
- SENSOR_ATTR_2(temp5_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp,
- 4, 2),
- SENSOR_ATTR_2(temp6_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp,
- 5, 2),
- SENSOR_ATTR_2(temp7_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp,
- 6, 2),
- SENSOR_ATTR_2(temp8_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp,
- 7, 2),
- SENSOR_ATTR_2(temp9_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp,
- 8, 2),
- SENSOR_ATTR_2(temp10_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp,
- 9, 2),
-};
+ if (nr == 3 && find_temp_source(data, temp, data->num_temp_beeps) < 0)
+ return 0; /* beep */
-static struct sensor_device_attribute_2 sda_temp_crit[] = {
- SENSOR_ATTR_2(temp1_crit, S_IRUGO | S_IWUSR, show_temp, store_temp,
- 0, 3),
- SENSOR_ATTR_2(temp2_crit, S_IRUGO | S_IWUSR, show_temp, store_temp,
- 1, 3),
- SENSOR_ATTR_2(temp3_crit, S_IRUGO | S_IWUSR, show_temp, store_temp,
- 2, 3),
- SENSOR_ATTR_2(temp4_crit, S_IRUGO | S_IWUSR, show_temp, store_temp,
- 3, 3),
- SENSOR_ATTR_2(temp5_crit, S_IRUGO | S_IWUSR, show_temp, store_temp,
- 4, 3),
- SENSOR_ATTR_2(temp6_crit, S_IRUGO | S_IWUSR, show_temp, store_temp,
- 5, 3),
- SENSOR_ATTR_2(temp7_crit, S_IRUGO | S_IWUSR, show_temp, store_temp,
- 6, 3),
- SENSOR_ATTR_2(temp8_crit, S_IRUGO | S_IWUSR, show_temp, store_temp,
- 7, 3),
- SENSOR_ATTR_2(temp9_crit, S_IRUGO | S_IWUSR, show_temp, store_temp,
- 8, 3),
- SENSOR_ATTR_2(temp10_crit, S_IRUGO | S_IWUSR, show_temp, store_temp,
- 9, 3),
-};
+ if (nr == 4 && !data->reg_temp[1][temp]) /* max */
+ return 0;
-static struct sensor_device_attribute sda_temp_offset[] = {
- SENSOR_ATTR(temp1_offset, S_IRUGO | S_IWUSR, show_temp_offset,
- store_temp_offset, 0),
- SENSOR_ATTR(temp2_offset, S_IRUGO | S_IWUSR, show_temp_offset,
- store_temp_offset, 1),
- SENSOR_ATTR(temp3_offset, S_IRUGO | S_IWUSR, show_temp_offset,
- store_temp_offset, 2),
- SENSOR_ATTR(temp4_offset, S_IRUGO | S_IWUSR, show_temp_offset,
- store_temp_offset, 3),
- SENSOR_ATTR(temp5_offset, S_IRUGO | S_IWUSR, show_temp_offset,
- store_temp_offset, 4),
- SENSOR_ATTR(temp6_offset, S_IRUGO | S_IWUSR, show_temp_offset,
- store_temp_offset, 5),
-};
+ if (nr == 5 && !data->reg_temp[2][temp]) /* max_hyst */
+ return 0;
+
+ if (nr == 6 && !data->reg_temp[3][temp]) /* crit */
+ return 0;
+
+ if (nr == 7 && !data->reg_temp[4][temp]) /* lcrit */
+ return 0;
+
+ /* offset and type only apply to fixed sensors */
+ if (nr > 7 && !(data->have_temp_fixed & (1 << temp)))
+ return 0;
-static struct sensor_device_attribute sda_temp_type[] = {
- SENSOR_ATTR(temp1_type, S_IRUGO | S_IWUSR, show_temp_type,
- store_temp_type, 0),
- SENSOR_ATTR(temp2_type, S_IRUGO | S_IWUSR, show_temp_type,
- store_temp_type, 1),
- SENSOR_ATTR(temp3_type, S_IRUGO | S_IWUSR, show_temp_type,
- store_temp_type, 2),
- SENSOR_ATTR(temp4_type, S_IRUGO | S_IWUSR, show_temp_type,
- store_temp_type, 3),
- SENSOR_ATTR(temp5_type, S_IRUGO | S_IWUSR, show_temp_type,
- store_temp_type, 4),
- SENSOR_ATTR(temp6_type, S_IRUGO | S_IWUSR, show_temp_type,
- store_temp_type, 5),
+ return attr->mode;
+}
+
+SENSOR_TEMPLATE_2(temp_input, "temp%d_input", S_IRUGO, show_temp, NULL, 0, 0);
+SENSOR_TEMPLATE(temp_label, "temp%d_label", S_IRUGO, show_temp_label, NULL, 0);
+SENSOR_TEMPLATE_2(temp_max, "temp%d_max", S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0, 1);
+SENSOR_TEMPLATE_2(temp_max_hyst, "temp%d_max_hyst", S_IRUGO | S_IWUSR,
+ show_temp, store_temp, 0, 2);
+SENSOR_TEMPLATE_2(temp_crit, "temp%d_crit", S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0, 3);
+SENSOR_TEMPLATE_2(temp_lcrit, "temp%d_lcrit", S_IRUGO | S_IWUSR, show_temp,
+ store_temp, 0, 4);
+SENSOR_TEMPLATE(temp_offset, "temp%d_offset", S_IRUGO | S_IWUSR,
+ show_temp_offset, store_temp_offset, 0);
+SENSOR_TEMPLATE(temp_type, "temp%d_type", S_IRUGO | S_IWUSR, show_temp_type,
+ store_temp_type, 0);
+SENSOR_TEMPLATE(temp_alarm, "temp%d_alarm", S_IRUGO, show_temp_alarm, NULL, 0);
+SENSOR_TEMPLATE(temp_beep, "temp%d_beep", S_IRUGO | S_IWUSR, show_temp_beep,
+ store_temp_beep, 0);
+
+/*
+ * nct6775_temp_is_visible uses the index into the following array
+ * to determine if attributes should be created or not.
+ * Any change in order or content must be matched.
+ */
+static struct sensor_device_template *nct6775_attributes_temp_template[] = {
+ &sensor_dev_template_temp_input,
+ &sensor_dev_template_temp_label,
+ &sensor_dev_template_temp_alarm, /* 2 */
+ &sensor_dev_template_temp_beep, /* 3 */
+ &sensor_dev_template_temp_max, /* 4 */
+ &sensor_dev_template_temp_max_hyst, /* 5 */
+ &sensor_dev_template_temp_crit, /* 6 */
+ &sensor_dev_template_temp_lcrit, /* 7 */
+ &sensor_dev_template_temp_offset, /* 8 */
+ &sensor_dev_template_temp_type, /* 9 */
+ NULL
};
-static struct sensor_device_attribute sda_temp_alarm[] = {
- SENSOR_ATTR(temp1_alarm, S_IRUGO, show_temp_alarm, NULL, 0),
- SENSOR_ATTR(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 1),
- SENSOR_ATTR(temp3_alarm, S_IRUGO, show_temp_alarm, NULL, 2),
- SENSOR_ATTR(temp4_alarm, S_IRUGO, show_temp_alarm, NULL, 3),
- SENSOR_ATTR(temp5_alarm, S_IRUGO, show_temp_alarm, NULL, 4),
- SENSOR_ATTR(temp6_alarm, S_IRUGO, show_temp_alarm, NULL, 5),
- SENSOR_ATTR(temp7_alarm, S_IRUGO, show_temp_alarm, NULL, 6),
- SENSOR_ATTR(temp8_alarm, S_IRUGO, show_temp_alarm, NULL, 7),
- SENSOR_ATTR(temp9_alarm, S_IRUGO, show_temp_alarm, NULL, 8),
- SENSOR_ATTR(temp10_alarm, S_IRUGO, show_temp_alarm, NULL, 9),
+static struct sensor_template_group nct6775_temp_template_group = {
+ .templates = nct6775_attributes_temp_template,
+ .is_visible = nct6775_temp_is_visible,
+ .base = 1,
};
static ssize_t
return count;
}
-static SENSOR_DEVICE_ATTR_2(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0, 0);
-static SENSOR_DEVICE_ATTR_2(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1, 0);
-static SENSOR_DEVICE_ATTR_2(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2, 0);
-static SENSOR_DEVICE_ATTR_2(pwm4, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3, 0);
-static SENSOR_DEVICE_ATTR_2(pwm5, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 4, 0);
-
-static SENSOR_DEVICE_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
- store_pwm_mode, 0);
-static SENSOR_DEVICE_ATTR(pwm2_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
- store_pwm_mode, 1);
-static SENSOR_DEVICE_ATTR(pwm3_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
- store_pwm_mode, 2);
-static SENSOR_DEVICE_ATTR(pwm4_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
- store_pwm_mode, 3);
-static SENSOR_DEVICE_ATTR(pwm5_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
- store_pwm_mode, 4);
-
-static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
- store_pwm_enable, 0);
-static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
- store_pwm_enable, 1);
-static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
- store_pwm_enable, 2);
-static SENSOR_DEVICE_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
- store_pwm_enable, 3);
-static SENSOR_DEVICE_ATTR(pwm5_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
- store_pwm_enable, 4);
-
-static SENSOR_DEVICE_ATTR(pwm1_temp_sel, S_IWUSR | S_IRUGO,
- show_pwm_temp_sel, store_pwm_temp_sel, 0);
-static SENSOR_DEVICE_ATTR(pwm2_temp_sel, S_IWUSR | S_IRUGO,
- show_pwm_temp_sel, store_pwm_temp_sel, 1);
-static SENSOR_DEVICE_ATTR(pwm3_temp_sel, S_IWUSR | S_IRUGO,
- show_pwm_temp_sel, store_pwm_temp_sel, 2);
-static SENSOR_DEVICE_ATTR(pwm4_temp_sel, S_IWUSR | S_IRUGO,
- show_pwm_temp_sel, store_pwm_temp_sel, 3);
-static SENSOR_DEVICE_ATTR(pwm5_temp_sel, S_IWUSR | S_IRUGO,
- show_pwm_temp_sel, store_pwm_temp_sel, 4);
-
-static SENSOR_DEVICE_ATTR(pwm1_target_temp, S_IWUSR | S_IRUGO, show_target_temp,
- store_target_temp, 0);
-static SENSOR_DEVICE_ATTR(pwm2_target_temp, S_IWUSR | S_IRUGO, show_target_temp,
- store_target_temp, 1);
-static SENSOR_DEVICE_ATTR(pwm3_target_temp, S_IWUSR | S_IRUGO, show_target_temp,
- store_target_temp, 2);
-static SENSOR_DEVICE_ATTR(pwm4_target_temp, S_IWUSR | S_IRUGO, show_target_temp,
- store_target_temp, 3);
-static SENSOR_DEVICE_ATTR(pwm5_target_temp, S_IWUSR | S_IRUGO, show_target_temp,
- store_target_temp, 4);
-
-static SENSOR_DEVICE_ATTR(fan1_target, S_IWUSR | S_IRUGO, show_target_speed,
- store_target_speed, 0);
-static SENSOR_DEVICE_ATTR(fan2_target, S_IWUSR | S_IRUGO, show_target_speed,
- store_target_speed, 1);
-static SENSOR_DEVICE_ATTR(fan3_target, S_IWUSR | S_IRUGO, show_target_speed,
- store_target_speed, 2);
-static SENSOR_DEVICE_ATTR(fan4_target, S_IWUSR | S_IRUGO, show_target_speed,
- store_target_speed, 3);
-static SENSOR_DEVICE_ATTR(fan5_target, S_IWUSR | S_IRUGO, show_target_speed,
- store_target_speed, 4);
-
-static SENSOR_DEVICE_ATTR(fan1_tolerance, S_IWUSR | S_IRUGO,
- show_speed_tolerance, store_speed_tolerance, 0);
-static SENSOR_DEVICE_ATTR(fan2_tolerance, S_IWUSR | S_IRUGO,
- show_speed_tolerance, store_speed_tolerance, 1);
-static SENSOR_DEVICE_ATTR(fan3_tolerance, S_IWUSR | S_IRUGO,
- show_speed_tolerance, store_speed_tolerance, 2);
-static SENSOR_DEVICE_ATTR(fan4_tolerance, S_IWUSR | S_IRUGO,
- show_speed_tolerance, store_speed_tolerance, 3);
-static SENSOR_DEVICE_ATTR(fan5_tolerance, S_IWUSR | S_IRUGO,
- show_speed_tolerance, store_speed_tolerance, 4);
+SENSOR_TEMPLATE_2(pwm, "pwm%d", S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0, 0);
+SENSOR_TEMPLATE(pwm_mode, "pwm%d_mode", S_IWUSR | S_IRUGO, show_pwm_mode,
+ store_pwm_mode, 0);
+SENSOR_TEMPLATE(pwm_enable, "pwm%d_enable", S_IWUSR | S_IRUGO, show_pwm_enable,
+ store_pwm_enable, 0);
+SENSOR_TEMPLATE(pwm_temp_sel, "pwm%d_temp_sel", S_IWUSR | S_IRUGO,
+ show_pwm_temp_sel, store_pwm_temp_sel, 0);
+SENSOR_TEMPLATE(pwm_target_temp, "pwm%d_target_temp", S_IWUSR | S_IRUGO,
+ show_target_temp, store_target_temp, 0);
+SENSOR_TEMPLATE(fan_target, "fan%d_target", S_IWUSR | S_IRUGO,
+ show_target_speed, store_target_speed, 0);
+SENSOR_TEMPLATE(fan_tolerance, "fan%d_tolerance", S_IWUSR | S_IRUGO,
+ show_speed_tolerance, store_speed_tolerance, 0);
/* Smart Fan registers */
return count;
}
-static SENSOR_DEVICE_ATTR(pwm1_weight_temp_sel, S_IWUSR | S_IRUGO,
- show_pwm_weight_temp_sel, store_pwm_weight_temp_sel,
- 0);
-static SENSOR_DEVICE_ATTR(pwm2_weight_temp_sel, S_IWUSR | S_IRUGO,
- show_pwm_weight_temp_sel, store_pwm_weight_temp_sel,
- 1);
-static SENSOR_DEVICE_ATTR(pwm3_weight_temp_sel, S_IWUSR | S_IRUGO,
- show_pwm_weight_temp_sel, store_pwm_weight_temp_sel,
- 2);
-static SENSOR_DEVICE_ATTR(pwm4_weight_temp_sel, S_IWUSR | S_IRUGO,
- show_pwm_weight_temp_sel, store_pwm_weight_temp_sel,
- 3);
-static SENSOR_DEVICE_ATTR(pwm5_weight_temp_sel, S_IWUSR | S_IRUGO,
- show_pwm_weight_temp_sel, store_pwm_weight_temp_sel,
- 4);
-
-static SENSOR_DEVICE_ATTR_2(pwm1_weight_temp_step, S_IWUSR | S_IRUGO,
- show_weight_temp, store_weight_temp, 0, 0);
-static SENSOR_DEVICE_ATTR_2(pwm2_weight_temp_step, S_IWUSR | S_IRUGO,
- show_weight_temp, store_weight_temp, 1, 0);
-static SENSOR_DEVICE_ATTR_2(pwm3_weight_temp_step, S_IWUSR | S_IRUGO,
- show_weight_temp, store_weight_temp, 2, 0);
-static SENSOR_DEVICE_ATTR_2(pwm4_weight_temp_step, S_IWUSR | S_IRUGO,
- show_weight_temp, store_weight_temp, 3, 0);
-static SENSOR_DEVICE_ATTR_2(pwm5_weight_temp_step, S_IWUSR | S_IRUGO,
- show_weight_temp, store_weight_temp, 4, 0);
-
-static SENSOR_DEVICE_ATTR_2(pwm1_weight_temp_step_tol, S_IWUSR | S_IRUGO,
- show_weight_temp, store_weight_temp, 0, 1);
-static SENSOR_DEVICE_ATTR_2(pwm2_weight_temp_step_tol, S_IWUSR | S_IRUGO,
- show_weight_temp, store_weight_temp, 1, 1);
-static SENSOR_DEVICE_ATTR_2(pwm3_weight_temp_step_tol, S_IWUSR | S_IRUGO,
- show_weight_temp, store_weight_temp, 2, 1);
-static SENSOR_DEVICE_ATTR_2(pwm4_weight_temp_step_tol, S_IWUSR | S_IRUGO,
- show_weight_temp, store_weight_temp, 3, 1);
-static SENSOR_DEVICE_ATTR_2(pwm5_weight_temp_step_tol, S_IWUSR | S_IRUGO,
- show_weight_temp, store_weight_temp, 4, 1);
-
-static SENSOR_DEVICE_ATTR_2(pwm1_weight_temp_step_base, S_IWUSR | S_IRUGO,
- show_weight_temp, store_weight_temp, 0, 2);
-static SENSOR_DEVICE_ATTR_2(pwm2_weight_temp_step_base, S_IWUSR | S_IRUGO,
- show_weight_temp, store_weight_temp, 1, 2);
-static SENSOR_DEVICE_ATTR_2(pwm3_weight_temp_step_base, S_IWUSR | S_IRUGO,
- show_weight_temp, store_weight_temp, 2, 2);
-static SENSOR_DEVICE_ATTR_2(pwm4_weight_temp_step_base, S_IWUSR | S_IRUGO,
- show_weight_temp, store_weight_temp, 3, 2);
-static SENSOR_DEVICE_ATTR_2(pwm5_weight_temp_step_base, S_IWUSR | S_IRUGO,
- show_weight_temp, store_weight_temp, 4, 2);
-
-static SENSOR_DEVICE_ATTR_2(pwm1_weight_duty_step, S_IWUSR | S_IRUGO,
- show_pwm, store_pwm, 0, 5);
-static SENSOR_DEVICE_ATTR_2(pwm2_weight_duty_step, S_IWUSR | S_IRUGO,
- show_pwm, store_pwm, 1, 5);
-static SENSOR_DEVICE_ATTR_2(pwm3_weight_duty_step, S_IWUSR | S_IRUGO,
- show_pwm, store_pwm, 2, 5);
-static SENSOR_DEVICE_ATTR_2(pwm4_weight_duty_step, S_IWUSR | S_IRUGO,
- show_pwm, store_pwm, 3, 5);
-static SENSOR_DEVICE_ATTR_2(pwm5_weight_duty_step, S_IWUSR | S_IRUGO,
- show_pwm, store_pwm, 4, 5);
-
-/* duty_base is not supported on all chips */
-static struct sensor_device_attribute_2 sda_weight_duty_base[] = {
- SENSOR_ATTR_2(pwm1_weight_duty_base, S_IWUSR | S_IRUGO,
- show_pwm, store_pwm, 0, 6),
- SENSOR_ATTR_2(pwm2_weight_duty_base, S_IWUSR | S_IRUGO,
- show_pwm, store_pwm, 1, 6),
- SENSOR_ATTR_2(pwm3_weight_duty_base, S_IWUSR | S_IRUGO,
- show_pwm, store_pwm, 2, 6),
- SENSOR_ATTR_2(pwm4_weight_duty_base, S_IWUSR | S_IRUGO,
- show_pwm, store_pwm, 3, 6),
- SENSOR_ATTR_2(pwm5_weight_duty_base, S_IWUSR | S_IRUGO,
- show_pwm, store_pwm, 4, 6),
-};
+SENSOR_TEMPLATE(pwm_weight_temp_sel, "pwm%d_weight_temp_sel", S_IWUSR | S_IRUGO,
+ show_pwm_weight_temp_sel, store_pwm_weight_temp_sel, 0);
+SENSOR_TEMPLATE_2(pwm_weight_temp_step, "pwm%d_weight_temp_step",
+ S_IWUSR | S_IRUGO, show_weight_temp, store_weight_temp, 0, 0);
+SENSOR_TEMPLATE_2(pwm_weight_temp_step_tol, "pwm%d_weight_temp_step_tol",
+ S_IWUSR | S_IRUGO, show_weight_temp, store_weight_temp, 0, 1);
+SENSOR_TEMPLATE_2(pwm_weight_temp_step_base, "pwm%d_weight_temp_step_base",
+ S_IWUSR | S_IRUGO, show_weight_temp, store_weight_temp, 0, 2);
+SENSOR_TEMPLATE_2(pwm_weight_duty_step, "pwm%d_weight_duty_step",
+ S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0, 5);
+SENSOR_TEMPLATE_2(pwm_weight_duty_base, "pwm%d_weight_duty_base",
+ S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0, 6);
static ssize_t
show_fan_time(struct device *dev, struct device_attribute *attr, char *buf)
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
-static SENSOR_DEVICE_ATTR_2(pwm1_stop_time, S_IWUSR | S_IRUGO, show_fan_time,
- store_fan_time, 0, 0);
-static SENSOR_DEVICE_ATTR_2(pwm2_stop_time, S_IWUSR | S_IRUGO, show_fan_time,
- store_fan_time, 1, 0);
-static SENSOR_DEVICE_ATTR_2(pwm3_stop_time, S_IWUSR | S_IRUGO, show_fan_time,
- store_fan_time, 2, 0);
-static SENSOR_DEVICE_ATTR_2(pwm4_stop_time, S_IWUSR | S_IRUGO, show_fan_time,
- store_fan_time, 3, 0);
-static SENSOR_DEVICE_ATTR_2(pwm5_stop_time, S_IWUSR | S_IRUGO, show_fan_time,
- store_fan_time, 4, 0);
-
-static SENSOR_DEVICE_ATTR_2(pwm1_step_up_time, S_IWUSR | S_IRUGO, show_fan_time,
- store_fan_time, 0, 1);
-static SENSOR_DEVICE_ATTR_2(pwm2_step_up_time, S_IWUSR | S_IRUGO, show_fan_time,
- store_fan_time, 1, 1);
-static SENSOR_DEVICE_ATTR_2(pwm3_step_up_time, S_IWUSR | S_IRUGO, show_fan_time,
- store_fan_time, 2, 1);
-static SENSOR_DEVICE_ATTR_2(pwm4_step_up_time, S_IWUSR | S_IRUGO, show_fan_time,
- store_fan_time, 3, 1);
-static SENSOR_DEVICE_ATTR_2(pwm5_step_up_time, S_IWUSR | S_IRUGO, show_fan_time,
- store_fan_time, 4, 1);
-
-static SENSOR_DEVICE_ATTR_2(pwm1_step_down_time, S_IWUSR | S_IRUGO,
- show_fan_time, store_fan_time, 0, 2);
-static SENSOR_DEVICE_ATTR_2(pwm2_step_down_time, S_IWUSR | S_IRUGO,
- show_fan_time, store_fan_time, 1, 2);
-static SENSOR_DEVICE_ATTR_2(pwm3_step_down_time, S_IWUSR | S_IRUGO,
- show_fan_time, store_fan_time, 2, 2);
-static SENSOR_DEVICE_ATTR_2(pwm4_step_down_time, S_IWUSR | S_IRUGO,
- show_fan_time, store_fan_time, 3, 2);
-static SENSOR_DEVICE_ATTR_2(pwm5_step_down_time, S_IWUSR | S_IRUGO,
- show_fan_time, store_fan_time, 4, 2);
-
-static SENSOR_DEVICE_ATTR_2(pwm1_start, S_IWUSR | S_IRUGO, show_pwm,
- store_pwm, 0, 1);
-static SENSOR_DEVICE_ATTR_2(pwm2_start, S_IWUSR | S_IRUGO, show_pwm,
- store_pwm, 1, 1);
-static SENSOR_DEVICE_ATTR_2(pwm3_start, S_IWUSR | S_IRUGO, show_pwm,
- store_pwm, 2, 1);
-static SENSOR_DEVICE_ATTR_2(pwm4_start, S_IWUSR | S_IRUGO, show_pwm,
- store_pwm, 3, 1);
-static SENSOR_DEVICE_ATTR_2(pwm5_start, S_IWUSR | S_IRUGO, show_pwm,
- store_pwm, 4, 1);
-
-static SENSOR_DEVICE_ATTR_2(pwm1_floor, S_IWUSR | S_IRUGO, show_pwm,
- store_pwm, 0, 2);
-static SENSOR_DEVICE_ATTR_2(pwm2_floor, S_IWUSR | S_IRUGO, show_pwm,
- store_pwm, 1, 2);
-static SENSOR_DEVICE_ATTR_2(pwm3_floor, S_IWUSR | S_IRUGO, show_pwm,
- store_pwm, 2, 2);
-static SENSOR_DEVICE_ATTR_2(pwm4_floor, S_IWUSR | S_IRUGO, show_pwm,
- store_pwm, 3, 2);
-static SENSOR_DEVICE_ATTR_2(pwm5_floor, S_IWUSR | S_IRUGO, show_pwm,
- store_pwm, 4, 2);
-
-static SENSOR_DEVICE_ATTR_2(pwm1_temp_tolerance, S_IWUSR | S_IRUGO,
- show_temp_tolerance, store_temp_tolerance, 0, 0);
-static SENSOR_DEVICE_ATTR_2(pwm2_temp_tolerance, S_IWUSR | S_IRUGO,
- show_temp_tolerance, store_temp_tolerance, 1, 0);
-static SENSOR_DEVICE_ATTR_2(pwm3_temp_tolerance, S_IWUSR | S_IRUGO,
- show_temp_tolerance, store_temp_tolerance, 2, 0);
-static SENSOR_DEVICE_ATTR_2(pwm4_temp_tolerance, S_IWUSR | S_IRUGO,
- show_temp_tolerance, store_temp_tolerance, 3, 0);
-static SENSOR_DEVICE_ATTR_2(pwm5_temp_tolerance, S_IWUSR | S_IRUGO,
- show_temp_tolerance, store_temp_tolerance, 4, 0);
-
-static SENSOR_DEVICE_ATTR_2(pwm1_crit_temp_tolerance, S_IWUSR | S_IRUGO,
- show_temp_tolerance, store_temp_tolerance, 0, 1);
-static SENSOR_DEVICE_ATTR_2(pwm2_crit_temp_tolerance, S_IWUSR | S_IRUGO,
- show_temp_tolerance, store_temp_tolerance, 1, 1);
-static SENSOR_DEVICE_ATTR_2(pwm3_crit_temp_tolerance, S_IWUSR | S_IRUGO,
- show_temp_tolerance, store_temp_tolerance, 2, 1);
-static SENSOR_DEVICE_ATTR_2(pwm4_crit_temp_tolerance, S_IWUSR | S_IRUGO,
- show_temp_tolerance, store_temp_tolerance, 3, 1);
-static SENSOR_DEVICE_ATTR_2(pwm5_crit_temp_tolerance, S_IWUSR | S_IRUGO,
- show_temp_tolerance, store_temp_tolerance, 4, 1);
-
-/* pwm_max is not supported on all chips */
-static struct sensor_device_attribute_2 sda_pwm_max[] = {
- SENSOR_ATTR_2(pwm1_max, S_IWUSR | S_IRUGO, show_pwm, store_pwm,
- 0, 3),
- SENSOR_ATTR_2(pwm2_max, S_IWUSR | S_IRUGO, show_pwm, store_pwm,
- 1, 3),
- SENSOR_ATTR_2(pwm3_max, S_IWUSR | S_IRUGO, show_pwm, store_pwm,
- 2, 3),
- SENSOR_ATTR_2(pwm4_max, S_IWUSR | S_IRUGO, show_pwm, store_pwm,
- 3, 3),
- SENSOR_ATTR_2(pwm5_max, S_IWUSR | S_IRUGO, show_pwm, store_pwm,
- 4, 3),
-};
-
-/* pwm_step is not supported on all chips */
-static struct sensor_device_attribute_2 sda_pwm_step[] = {
- SENSOR_ATTR_2(pwm1_step, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0, 4),
- SENSOR_ATTR_2(pwm2_step, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1, 4),
- SENSOR_ATTR_2(pwm3_step, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2, 4),
- SENSOR_ATTR_2(pwm4_step, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3, 4),
- SENSOR_ATTR_2(pwm5_step, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 4, 4),
-};
-
-static struct attribute *nct6775_attributes_pwm[5][20] = {
- {
- &sensor_dev_attr_pwm1.dev_attr.attr,
- &sensor_dev_attr_pwm1_mode.dev_attr.attr,
- &sensor_dev_attr_pwm1_enable.dev_attr.attr,
- &sensor_dev_attr_pwm1_temp_sel.dev_attr.attr,
- &sensor_dev_attr_pwm1_temp_tolerance.dev_attr.attr,
- &sensor_dev_attr_pwm1_crit_temp_tolerance.dev_attr.attr,
- &sensor_dev_attr_pwm1_target_temp.dev_attr.attr,
- &sensor_dev_attr_fan1_target.dev_attr.attr,
- &sensor_dev_attr_fan1_tolerance.dev_attr.attr,
- &sensor_dev_attr_pwm1_stop_time.dev_attr.attr,
- &sensor_dev_attr_pwm1_step_up_time.dev_attr.attr,
- &sensor_dev_attr_pwm1_step_down_time.dev_attr.attr,
- &sensor_dev_attr_pwm1_start.dev_attr.attr,
- &sensor_dev_attr_pwm1_floor.dev_attr.attr,
- &sensor_dev_attr_pwm1_weight_temp_sel.dev_attr.attr,
- &sensor_dev_attr_pwm1_weight_temp_step.dev_attr.attr,
- &sensor_dev_attr_pwm1_weight_temp_step_tol.dev_attr.attr,
- &sensor_dev_attr_pwm1_weight_temp_step_base.dev_attr.attr,
- &sensor_dev_attr_pwm1_weight_duty_step.dev_attr.attr,
- NULL
- },
- {
- &sensor_dev_attr_pwm2.dev_attr.attr,
- &sensor_dev_attr_pwm2_mode.dev_attr.attr,
- &sensor_dev_attr_pwm2_enable.dev_attr.attr,
- &sensor_dev_attr_pwm2_temp_sel.dev_attr.attr,
- &sensor_dev_attr_pwm2_temp_tolerance.dev_attr.attr,
- &sensor_dev_attr_pwm2_crit_temp_tolerance.dev_attr.attr,
- &sensor_dev_attr_pwm2_target_temp.dev_attr.attr,
- &sensor_dev_attr_fan2_target.dev_attr.attr,
- &sensor_dev_attr_fan2_tolerance.dev_attr.attr,
- &sensor_dev_attr_pwm2_stop_time.dev_attr.attr,
- &sensor_dev_attr_pwm2_step_up_time.dev_attr.attr,
- &sensor_dev_attr_pwm2_step_down_time.dev_attr.attr,
- &sensor_dev_attr_pwm2_start.dev_attr.attr,
- &sensor_dev_attr_pwm2_floor.dev_attr.attr,
- &sensor_dev_attr_pwm2_weight_temp_sel.dev_attr.attr,
- &sensor_dev_attr_pwm2_weight_temp_step.dev_attr.attr,
- &sensor_dev_attr_pwm2_weight_temp_step_tol.dev_attr.attr,
- &sensor_dev_attr_pwm2_weight_temp_step_base.dev_attr.attr,
- &sensor_dev_attr_pwm2_weight_duty_step.dev_attr.attr,
- NULL
- },
- {
- &sensor_dev_attr_pwm3.dev_attr.attr,
- &sensor_dev_attr_pwm3_mode.dev_attr.attr,
- &sensor_dev_attr_pwm3_enable.dev_attr.attr,
- &sensor_dev_attr_pwm3_temp_sel.dev_attr.attr,
- &sensor_dev_attr_pwm3_temp_tolerance.dev_attr.attr,
- &sensor_dev_attr_pwm3_crit_temp_tolerance.dev_attr.attr,
- &sensor_dev_attr_pwm3_target_temp.dev_attr.attr,
- &sensor_dev_attr_fan3_target.dev_attr.attr,
- &sensor_dev_attr_fan3_tolerance.dev_attr.attr,
- &sensor_dev_attr_pwm3_stop_time.dev_attr.attr,
- &sensor_dev_attr_pwm3_step_up_time.dev_attr.attr,
- &sensor_dev_attr_pwm3_step_down_time.dev_attr.attr,
- &sensor_dev_attr_pwm3_start.dev_attr.attr,
- &sensor_dev_attr_pwm3_floor.dev_attr.attr,
- &sensor_dev_attr_pwm3_weight_temp_sel.dev_attr.attr,
- &sensor_dev_attr_pwm3_weight_temp_step.dev_attr.attr,
- &sensor_dev_attr_pwm3_weight_temp_step_tol.dev_attr.attr,
- &sensor_dev_attr_pwm3_weight_temp_step_base.dev_attr.attr,
- &sensor_dev_attr_pwm3_weight_duty_step.dev_attr.attr,
- NULL
- },
- {
- &sensor_dev_attr_pwm4.dev_attr.attr,
- &sensor_dev_attr_pwm4_mode.dev_attr.attr,
- &sensor_dev_attr_pwm4_enable.dev_attr.attr,
- &sensor_dev_attr_pwm4_temp_sel.dev_attr.attr,
- &sensor_dev_attr_pwm4_temp_tolerance.dev_attr.attr,
- &sensor_dev_attr_pwm4_crit_temp_tolerance.dev_attr.attr,
- &sensor_dev_attr_pwm4_target_temp.dev_attr.attr,
- &sensor_dev_attr_fan4_target.dev_attr.attr,
- &sensor_dev_attr_fan4_tolerance.dev_attr.attr,
- &sensor_dev_attr_pwm4_stop_time.dev_attr.attr,
- &sensor_dev_attr_pwm4_step_up_time.dev_attr.attr,
- &sensor_dev_attr_pwm4_step_down_time.dev_attr.attr,
- &sensor_dev_attr_pwm4_start.dev_attr.attr,
- &sensor_dev_attr_pwm4_floor.dev_attr.attr,
- &sensor_dev_attr_pwm4_weight_temp_sel.dev_attr.attr,
- &sensor_dev_attr_pwm4_weight_temp_step.dev_attr.attr,
- &sensor_dev_attr_pwm4_weight_temp_step_tol.dev_attr.attr,
- &sensor_dev_attr_pwm4_weight_temp_step_base.dev_attr.attr,
- &sensor_dev_attr_pwm4_weight_duty_step.dev_attr.attr,
- NULL
- },
- {
- &sensor_dev_attr_pwm5.dev_attr.attr,
- &sensor_dev_attr_pwm5_mode.dev_attr.attr,
- &sensor_dev_attr_pwm5_enable.dev_attr.attr,
- &sensor_dev_attr_pwm5_temp_sel.dev_attr.attr,
- &sensor_dev_attr_pwm5_temp_tolerance.dev_attr.attr,
- &sensor_dev_attr_pwm5_crit_temp_tolerance.dev_attr.attr,
- &sensor_dev_attr_pwm5_target_temp.dev_attr.attr,
- &sensor_dev_attr_fan5_target.dev_attr.attr,
- &sensor_dev_attr_fan5_tolerance.dev_attr.attr,
- &sensor_dev_attr_pwm5_stop_time.dev_attr.attr,
- &sensor_dev_attr_pwm5_step_up_time.dev_attr.attr,
- &sensor_dev_attr_pwm5_step_down_time.dev_attr.attr,
- &sensor_dev_attr_pwm5_start.dev_attr.attr,
- &sensor_dev_attr_pwm5_floor.dev_attr.attr,
- &sensor_dev_attr_pwm5_weight_temp_sel.dev_attr.attr,
- &sensor_dev_attr_pwm5_weight_temp_step.dev_attr.attr,
- &sensor_dev_attr_pwm5_weight_temp_step_tol.dev_attr.attr,
- &sensor_dev_attr_pwm5_weight_temp_step_base.dev_attr.attr,
- &sensor_dev_attr_pwm5_weight_duty_step.dev_attr.attr,
- NULL
- },
-};
-
-static const struct attribute_group nct6775_group_pwm[5] = {
- { .attrs = nct6775_attributes_pwm[0] },
- { .attrs = nct6775_attributes_pwm[1] },
- { .attrs = nct6775_attributes_pwm[2] },
- { .attrs = nct6775_attributes_pwm[3] },
- { .attrs = nct6775_attributes_pwm[4] },
-};
-
static ssize_t
show_auto_pwm(struct device *dev, struct device_attribute *attr, char *buf)
{
break;
case nct6776:
break; /* always enabled, nothing to do */
+ case nct6106:
case nct6779:
- nct6775_write_value(data, NCT6779_REG_CRITICAL_PWM[nr],
+ case nct6791:
+ nct6775_write_value(data, data->REG_CRITICAL_PWM[nr],
val);
reg = nct6775_read_value(data,
- NCT6779_REG_CRITICAL_PWM_ENABLE[nr]);
+ data->REG_CRITICAL_PWM_ENABLE[nr]);
if (val == 255)
- reg &= ~0x01;
+ reg &= ~data->CRITICAL_PWM_ENABLE_MASK;
else
- reg |= 0x01;
+ reg |= data->CRITICAL_PWM_ENABLE_MASK;
nct6775_write_value(data,
- NCT6779_REG_CRITICAL_PWM_ENABLE[nr],
+ data->REG_CRITICAL_PWM_ENABLE[nr],
reg);
break;
}
return count;
}
+static umode_t nct6775_pwm_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ int pwm = index / 36; /* pwm index */
+ int nr = index % 36; /* attribute index */
+
+ if (!(data->has_pwm & (1 << pwm)))
+ return 0;
+
+ if (nr == 19 && data->REG_PWM[3] == NULL) /* pwm_max */
+ return 0;
+ if (nr == 20 && data->REG_PWM[4] == NULL) /* pwm_step */
+ return 0;
+ if (nr == 21 && data->REG_PWM[6] == NULL) /* weight_duty_base */
+ return 0;
+
+ if (nr >= 22 && nr <= 35) { /* auto point */
+ int api = (nr - 22) / 2; /* auto point index */
+
+ if (api > data->auto_pwm_num)
+ return 0;
+ }
+ return attr->mode;
+}
+
+SENSOR_TEMPLATE_2(pwm_stop_time, "pwm%d_stop_time", S_IWUSR | S_IRUGO,
+ show_fan_time, store_fan_time, 0, 0);
+SENSOR_TEMPLATE_2(pwm_step_up_time, "pwm%d_step_up_time", S_IWUSR | S_IRUGO,
+ show_fan_time, store_fan_time, 0, 1);
+SENSOR_TEMPLATE_2(pwm_step_down_time, "pwm%d_step_down_time", S_IWUSR | S_IRUGO,
+ show_fan_time, store_fan_time, 0, 2);
+SENSOR_TEMPLATE_2(pwm_start, "pwm%d_start", S_IWUSR | S_IRUGO, show_pwm,
+ store_pwm, 0, 1);
+SENSOR_TEMPLATE_2(pwm_floor, "pwm%d_floor", S_IWUSR | S_IRUGO, show_pwm,
+ store_pwm, 0, 2);
+SENSOR_TEMPLATE_2(pwm_temp_tolerance, "pwm%d_temp_tolerance", S_IWUSR | S_IRUGO,
+ show_temp_tolerance, store_temp_tolerance, 0, 0);
+SENSOR_TEMPLATE_2(pwm_crit_temp_tolerance, "pwm%d_crit_temp_tolerance",
+ S_IWUSR | S_IRUGO, show_temp_tolerance, store_temp_tolerance,
+ 0, 1);
+
+SENSOR_TEMPLATE_2(pwm_max, "pwm%d_max", S_IWUSR | S_IRUGO, show_pwm, store_pwm,
+ 0, 3);
+
+SENSOR_TEMPLATE_2(pwm_step, "pwm%d_step", S_IWUSR | S_IRUGO, show_pwm,
+ store_pwm, 0, 4);
+
+SENSOR_TEMPLATE_2(pwm_auto_point1_pwm, "pwm%d_auto_point1_pwm",
+ S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 0);
+SENSOR_TEMPLATE_2(pwm_auto_point1_temp, "pwm%d_auto_point1_temp",
+ S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 0);
+
+SENSOR_TEMPLATE_2(pwm_auto_point2_pwm, "pwm%d_auto_point2_pwm",
+ S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 1);
+SENSOR_TEMPLATE_2(pwm_auto_point2_temp, "pwm%d_auto_point2_temp",
+ S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 1);
+
+SENSOR_TEMPLATE_2(pwm_auto_point3_pwm, "pwm%d_auto_point3_pwm",
+ S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 2);
+SENSOR_TEMPLATE_2(pwm_auto_point3_temp, "pwm%d_auto_point3_temp",
+ S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 2);
+
+SENSOR_TEMPLATE_2(pwm_auto_point4_pwm, "pwm%d_auto_point4_pwm",
+ S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 3);
+SENSOR_TEMPLATE_2(pwm_auto_point4_temp, "pwm%d_auto_point4_temp",
+ S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 3);
+
+SENSOR_TEMPLATE_2(pwm_auto_point5_pwm, "pwm%d_auto_point5_pwm",
+ S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 4);
+SENSOR_TEMPLATE_2(pwm_auto_point5_temp, "pwm%d_auto_point5_temp",
+ S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 4);
+
+SENSOR_TEMPLATE_2(pwm_auto_point6_pwm, "pwm%d_auto_point6_pwm",
+ S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 5);
+SENSOR_TEMPLATE_2(pwm_auto_point6_temp, "pwm%d_auto_point6_temp",
+ S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 5);
+
+SENSOR_TEMPLATE_2(pwm_auto_point7_pwm, "pwm%d_auto_point7_pwm",
+ S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 6);
+SENSOR_TEMPLATE_2(pwm_auto_point7_temp, "pwm%d_auto_point7_temp",
+ S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 6);
+
/*
- * The number of auto-point trip points is chip dependent.
- * Need to check support while generating/removing attribute files.
+ * nct6775_pwm_is_visible uses the index into the following array
+ * to determine if attributes should be created or not.
+ * Any change in order or content must be matched.
*/
-static struct sensor_device_attribute_2 sda_auto_pwm_arrays[] = {
- SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 0, 0),
- SENSOR_ATTR_2(pwm1_auto_point1_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 0, 0),
- SENSOR_ATTR_2(pwm1_auto_point2_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 0, 1),
- SENSOR_ATTR_2(pwm1_auto_point2_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 0, 1),
- SENSOR_ATTR_2(pwm1_auto_point3_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 0, 2),
- SENSOR_ATTR_2(pwm1_auto_point3_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 0, 2),
- SENSOR_ATTR_2(pwm1_auto_point4_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 0, 3),
- SENSOR_ATTR_2(pwm1_auto_point4_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 0, 3),
- SENSOR_ATTR_2(pwm1_auto_point5_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 0, 4),
- SENSOR_ATTR_2(pwm1_auto_point5_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 0, 4),
- SENSOR_ATTR_2(pwm1_auto_point6_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 0, 5),
- SENSOR_ATTR_2(pwm1_auto_point6_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 0, 5),
- SENSOR_ATTR_2(pwm1_auto_point7_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 0, 6),
- SENSOR_ATTR_2(pwm1_auto_point7_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 0, 6),
-
- SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 1, 0),
- SENSOR_ATTR_2(pwm2_auto_point1_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 1, 0),
- SENSOR_ATTR_2(pwm2_auto_point2_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 1, 1),
- SENSOR_ATTR_2(pwm2_auto_point2_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 1, 1),
- SENSOR_ATTR_2(pwm2_auto_point3_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 1, 2),
- SENSOR_ATTR_2(pwm2_auto_point3_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 1, 2),
- SENSOR_ATTR_2(pwm2_auto_point4_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 1, 3),
- SENSOR_ATTR_2(pwm2_auto_point4_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 1, 3),
- SENSOR_ATTR_2(pwm2_auto_point5_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 1, 4),
- SENSOR_ATTR_2(pwm2_auto_point5_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 1, 4),
- SENSOR_ATTR_2(pwm2_auto_point6_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 1, 5),
- SENSOR_ATTR_2(pwm2_auto_point6_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 1, 5),
- SENSOR_ATTR_2(pwm2_auto_point7_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 1, 6),
- SENSOR_ATTR_2(pwm2_auto_point7_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 1, 6),
-
- SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 2, 0),
- SENSOR_ATTR_2(pwm3_auto_point1_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 2, 0),
- SENSOR_ATTR_2(pwm3_auto_point2_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 2, 1),
- SENSOR_ATTR_2(pwm3_auto_point2_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 2, 1),
- SENSOR_ATTR_2(pwm3_auto_point3_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 2, 2),
- SENSOR_ATTR_2(pwm3_auto_point3_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 2, 2),
- SENSOR_ATTR_2(pwm3_auto_point4_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 2, 3),
- SENSOR_ATTR_2(pwm3_auto_point4_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 2, 3),
- SENSOR_ATTR_2(pwm3_auto_point5_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 2, 4),
- SENSOR_ATTR_2(pwm3_auto_point5_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 2, 4),
- SENSOR_ATTR_2(pwm3_auto_point6_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 2, 5),
- SENSOR_ATTR_2(pwm3_auto_point6_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 2, 5),
- SENSOR_ATTR_2(pwm3_auto_point7_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 2, 6),
- SENSOR_ATTR_2(pwm3_auto_point7_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 2, 6),
-
- SENSOR_ATTR_2(pwm4_auto_point1_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 3, 0),
- SENSOR_ATTR_2(pwm4_auto_point1_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 3, 0),
- SENSOR_ATTR_2(pwm4_auto_point2_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 3, 1),
- SENSOR_ATTR_2(pwm4_auto_point2_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 3, 1),
- SENSOR_ATTR_2(pwm4_auto_point3_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 3, 2),
- SENSOR_ATTR_2(pwm4_auto_point3_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 3, 2),
- SENSOR_ATTR_2(pwm4_auto_point4_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 3, 3),
- SENSOR_ATTR_2(pwm4_auto_point4_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 3, 3),
- SENSOR_ATTR_2(pwm4_auto_point5_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 3, 4),
- SENSOR_ATTR_2(pwm4_auto_point5_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 3, 4),
- SENSOR_ATTR_2(pwm4_auto_point6_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 3, 5),
- SENSOR_ATTR_2(pwm4_auto_point6_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 3, 5),
- SENSOR_ATTR_2(pwm4_auto_point7_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 3, 6),
- SENSOR_ATTR_2(pwm4_auto_point7_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 3, 6),
-
- SENSOR_ATTR_2(pwm5_auto_point1_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 4, 0),
- SENSOR_ATTR_2(pwm5_auto_point1_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 4, 0),
- SENSOR_ATTR_2(pwm5_auto_point2_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 4, 1),
- SENSOR_ATTR_2(pwm5_auto_point2_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 4, 1),
- SENSOR_ATTR_2(pwm5_auto_point3_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 4, 2),
- SENSOR_ATTR_2(pwm5_auto_point3_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 4, 2),
- SENSOR_ATTR_2(pwm5_auto_point4_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 4, 3),
- SENSOR_ATTR_2(pwm5_auto_point4_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 4, 3),
- SENSOR_ATTR_2(pwm5_auto_point5_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 4, 4),
- SENSOR_ATTR_2(pwm5_auto_point5_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 4, 4),
- SENSOR_ATTR_2(pwm5_auto_point6_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 4, 5),
- SENSOR_ATTR_2(pwm5_auto_point6_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 4, 5),
- SENSOR_ATTR_2(pwm5_auto_point7_pwm, S_IWUSR | S_IRUGO,
- show_auto_pwm, store_auto_pwm, 4, 6),
- SENSOR_ATTR_2(pwm5_auto_point7_temp, S_IWUSR | S_IRUGO,
- show_auto_temp, store_auto_temp, 4, 6),
+static struct sensor_device_template *nct6775_attributes_pwm_template[] = {
+ &sensor_dev_template_pwm,
+ &sensor_dev_template_pwm_mode,
+ &sensor_dev_template_pwm_enable,
+ &sensor_dev_template_pwm_temp_sel,
+ &sensor_dev_template_pwm_temp_tolerance,
+ &sensor_dev_template_pwm_crit_temp_tolerance,
+ &sensor_dev_template_pwm_target_temp,
+ &sensor_dev_template_fan_target,
+ &sensor_dev_template_fan_tolerance,
+ &sensor_dev_template_pwm_stop_time,
+ &sensor_dev_template_pwm_step_up_time,
+ &sensor_dev_template_pwm_step_down_time,
+ &sensor_dev_template_pwm_start,
+ &sensor_dev_template_pwm_floor,
+ &sensor_dev_template_pwm_weight_temp_sel,
+ &sensor_dev_template_pwm_weight_temp_step,
+ &sensor_dev_template_pwm_weight_temp_step_tol,
+ &sensor_dev_template_pwm_weight_temp_step_base,
+ &sensor_dev_template_pwm_weight_duty_step,
+ &sensor_dev_template_pwm_max, /* 19 */
+ &sensor_dev_template_pwm_step, /* 20 */
+ &sensor_dev_template_pwm_weight_duty_base, /* 21 */
+ &sensor_dev_template_pwm_auto_point1_pwm, /* 22 */
+ &sensor_dev_template_pwm_auto_point1_temp,
+ &sensor_dev_template_pwm_auto_point2_pwm,
+ &sensor_dev_template_pwm_auto_point2_temp,
+ &sensor_dev_template_pwm_auto_point3_pwm,
+ &sensor_dev_template_pwm_auto_point3_temp,
+ &sensor_dev_template_pwm_auto_point4_pwm,
+ &sensor_dev_template_pwm_auto_point4_temp,
+ &sensor_dev_template_pwm_auto_point5_pwm,
+ &sensor_dev_template_pwm_auto_point5_temp,
+ &sensor_dev_template_pwm_auto_point6_pwm,
+ &sensor_dev_template_pwm_auto_point6_temp,
+ &sensor_dev_template_pwm_auto_point7_pwm,
+ &sensor_dev_template_pwm_auto_point7_temp, /* 35 */
+
+ NULL
+};
+
+static struct sensor_template_group nct6775_pwm_template_group = {
+ .templates = nct6775_attributes_pwm_template,
+ .is_visible = nct6775_pwm_is_visible,
+ .base = 1,
};
static ssize_t
const char *buf, size_t count)
{
struct nct6775_data *data = dev_get_drvdata(dev);
- struct nct6775_sio_data *sio_data = dev->platform_data;
int nr = to_sensor_dev_attr(attr)->index - INTRUSION_ALARM_BASE;
unsigned long val;
u8 reg;
* The CR registers are the same for all chips, and not all chips
* support clearing the caseopen status through "regular" registers.
*/
- ret = superio_enter(sio_data->sioreg);
+ ret = superio_enter(data->sioreg);
if (ret) {
count = ret;
goto error;
}
- superio_select(sio_data->sioreg, NCT6775_LD_ACPI);
- reg = superio_inb(sio_data->sioreg, NCT6775_REG_CR_CASEOPEN_CLR[nr]);
+ superio_select(data->sioreg, NCT6775_LD_ACPI);
+ reg = superio_inb(data->sioreg, NCT6775_REG_CR_CASEOPEN_CLR[nr]);
reg |= NCT6775_CR_CASEOPEN_CLR_MASK[nr];
- superio_outb(sio_data->sioreg, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg);
+ superio_outb(data->sioreg, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg);
reg &= ~NCT6775_CR_CASEOPEN_CLR_MASK[nr];
- superio_outb(sio_data->sioreg, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg);
- superio_exit(sio_data->sioreg);
+ superio_outb(data->sioreg, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg);
+ superio_exit(data->sioreg);
data->valid = false; /* Force cache refresh */
error:
return count;
}
-static struct sensor_device_attribute sda_caseopen[] = {
- SENSOR_ATTR(intrusion0_alarm, S_IWUSR | S_IRUGO, show_alarm,
- clear_caseopen, INTRUSION_ALARM_BASE),
- SENSOR_ATTR(intrusion1_alarm, S_IWUSR | S_IRUGO, show_alarm,
- clear_caseopen, INTRUSION_ALARM_BASE + 1),
-};
-
-/*
- * Driver and device management
- */
-
-static void nct6775_device_remove_files(struct device *dev)
+static SENSOR_DEVICE_ATTR(intrusion0_alarm, S_IWUSR | S_IRUGO, show_alarm,
+ clear_caseopen, INTRUSION_ALARM_BASE);
+static SENSOR_DEVICE_ATTR(intrusion1_alarm, S_IWUSR | S_IRUGO, show_alarm,
+ clear_caseopen, INTRUSION_ALARM_BASE + 1);
+static SENSOR_DEVICE_ATTR(intrusion0_beep, S_IWUSR | S_IRUGO, show_beep,
+ store_beep, INTRUSION_ALARM_BASE);
+static SENSOR_DEVICE_ATTR(intrusion1_beep, S_IWUSR | S_IRUGO, show_beep,
+ store_beep, INTRUSION_ALARM_BASE + 1);
+static SENSOR_DEVICE_ATTR(beep_enable, S_IWUSR | S_IRUGO, show_beep,
+ store_beep, BEEP_ENABLE_BASE);
+
+static umode_t nct6775_other_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
{
- /*
- * some entries in the following arrays may not have been used in
- * device_create_file(), but device_remove_file() will ignore them
- */
- int i;
+ struct device *dev = container_of(kobj, struct device, kobj);
struct nct6775_data *data = dev_get_drvdata(dev);
- for (i = 0; i < data->pwm_num; i++)
- sysfs_remove_group(&dev->kobj, &nct6775_group_pwm[i]);
+ if (index == 1 && !data->have_vid)
+ return 0;
- for (i = 0; i < ARRAY_SIZE(sda_pwm_max); i++)
- device_remove_file(dev, &sda_pwm_max[i].dev_attr);
+ if (index == 2 || index == 3) {
+ if (data->ALARM_BITS[INTRUSION_ALARM_BASE + index - 2] < 0)
+ return 0;
+ }
+
+ if (index == 4 || index == 5) {
+ if (data->BEEP_BITS[INTRUSION_ALARM_BASE + index - 4] < 0)
+ return 0;
+ }
- for (i = 0; i < ARRAY_SIZE(sda_pwm_step); i++)
- device_remove_file(dev, &sda_pwm_step[i].dev_attr);
+ return attr->mode;
+}
- for (i = 0; i < ARRAY_SIZE(sda_weight_duty_base); i++)
- device_remove_file(dev, &sda_weight_duty_base[i].dev_attr);
+/*
+ * nct6775_other_is_visible uses the index into the following array
+ * to determine if attributes should be created or not.
+ * Any change in order or content must be matched.
+ */
+static struct attribute *nct6775_attributes_other[] = {
+ &dev_attr_name.attr,
+ &dev_attr_cpu0_vid.attr, /* 1 */
+ &sensor_dev_attr_intrusion0_alarm.dev_attr.attr, /* 2 */
+ &sensor_dev_attr_intrusion1_alarm.dev_attr.attr, /* 3 */
+ &sensor_dev_attr_intrusion0_beep.dev_attr.attr, /* 4 */
+ &sensor_dev_attr_intrusion1_beep.dev_attr.attr, /* 5 */
+ &sensor_dev_attr_beep_enable.dev_attr.attr, /* 6 */
+
+ NULL
+};
- for (i = 0; i < ARRAY_SIZE(sda_auto_pwm_arrays); i++)
- device_remove_file(dev, &sda_auto_pwm_arrays[i].dev_attr);
+static const struct attribute_group nct6775_group_other = {
+ .attrs = nct6775_attributes_other,
+ .is_visible = nct6775_other_is_visible,
+};
- for (i = 0; i < data->in_num; i++)
- sysfs_remove_group(&dev->kobj, &nct6775_group_in[i]);
+/*
+ * Driver and device management
+ */
- for (i = 0; i < 5; i++) {
- device_remove_file(dev, &sda_fan_input[i].dev_attr);
- device_remove_file(dev, &sda_fan_alarm[i].dev_attr);
- device_remove_file(dev, &sda_fan_div[i].dev_attr);
- device_remove_file(dev, &sda_fan_min[i].dev_attr);
- device_remove_file(dev, &sda_fan_pulses[i].dev_attr);
- }
- for (i = 0; i < NUM_TEMP; i++) {
- if (!(data->have_temp & (1 << i)))
- continue;
- device_remove_file(dev, &sda_temp_input[i].dev_attr);
- device_remove_file(dev, &sda_temp_label[i].dev_attr);
- device_remove_file(dev, &sda_temp_max[i].dev_attr);
- device_remove_file(dev, &sda_temp_max_hyst[i].dev_attr);
- device_remove_file(dev, &sda_temp_crit[i].dev_attr);
- device_remove_file(dev, &sda_temp_alarm[i].dev_attr);
- if (!(data->have_temp_fixed & (1 << i)))
- continue;
- device_remove_file(dev, &sda_temp_type[i].dev_attr);
- device_remove_file(dev, &sda_temp_offset[i].dev_attr);
- }
+static void nct6775_device_remove_files(struct device *dev)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
- device_remove_file(dev, &sda_caseopen[0].dev_attr);
- device_remove_file(dev, &sda_caseopen[1].dev_attr);
+ if (data->group_pwm)
+ sysfs_remove_group(&dev->kobj, data->group_pwm);
+ if (data->group_in)
+ sysfs_remove_group(&dev->kobj, data->group_in);
+ if (data->group_fan)
+ sysfs_remove_group(&dev->kobj, data->group_fan);
+ if (data->group_temp)
+ sysfs_remove_group(&dev->kobj, data->group_temp);
- device_remove_file(dev, &dev_attr_name);
- device_remove_file(dev, &dev_attr_cpu0_vid);
+ sysfs_remove_group(&dev->kobj, &nct6775_group_other);
}
/* Get the monitoring functions started */
for (i = 0; i < data->temp_fixed_num; i++) {
if (!(data->have_temp_fixed & (1 << i)))
continue;
- if ((tmp & (0x02 << i))) /* diode */
- data->temp_type[i] = 3 - ((diode >> i) & 0x02);
+ if ((tmp & (data->DIODE_MASK << i))) /* diode */
+ data->temp_type[i]
+ = 3 - ((diode >> i) & data->DIODE_MASK);
else /* thermistor */
data->temp_type[i] = 4;
}
}
-static int
-nct6775_check_fan_inputs(const struct nct6775_sio_data *sio_data,
- struct nct6775_data *data)
+static void
+nct6775_check_fan_inputs(struct nct6775_data *data)
{
+ bool fan3pin, fan4pin, fan4min, fan5pin, fan6pin;
+ bool pwm3pin, pwm4pin, pwm5pin, pwm6pin;
+ int sioreg = data->sioreg;
int regval;
- bool fan3pin, fan3min, fan4pin, fan4min, fan5pin;
- bool pwm3pin, pwm4pin, pwm5pin;
- int ret;
-
- ret = superio_enter(sio_data->sioreg);
- if (ret)
- return ret;
/* fan4 and fan5 share some pins with the GPIO and serial flash */
if (data->kind == nct6775) {
- regval = superio_inb(sio_data->sioreg, 0x2c);
+ regval = superio_inb(sioreg, 0x2c);
fan3pin = regval & (1 << 6);
- fan3min = fan3pin;
pwm3pin = regval & (1 << 7);
/* On NCT6775, fan4 shares pins with the fdc interface */
- fan4pin = !(superio_inb(sio_data->sioreg, 0x2A) & 0x80);
- fan4min = 0;
- fan5pin = 0;
- pwm4pin = 0;
- pwm5pin = 0;
+ fan4pin = !(superio_inb(sioreg, 0x2A) & 0x80);
+ fan4min = false;
+ fan5pin = false;
+ fan6pin = false;
+ pwm4pin = false;
+ pwm5pin = false;
+ pwm6pin = false;
} else if (data->kind == nct6776) {
- bool gpok = superio_inb(sio_data->sioreg, 0x27) & 0x80;
+ bool gpok = superio_inb(sioreg, 0x27) & 0x80;
- superio_select(sio_data->sioreg, NCT6775_LD_HWM);
- regval = superio_inb(sio_data->sioreg, SIO_REG_ENABLE);
+ superio_select(sioreg, NCT6775_LD_HWM);
+ regval = superio_inb(sioreg, SIO_REG_ENABLE);
if (regval & 0x80)
fan3pin = gpok;
else
- fan3pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x40);
+ fan3pin = !(superio_inb(sioreg, 0x24) & 0x40);
if (regval & 0x40)
fan4pin = gpok;
else
- fan4pin = superio_inb(sio_data->sioreg, 0x1C) & 0x01;
+ fan4pin = superio_inb(sioreg, 0x1C) & 0x01;
if (regval & 0x20)
fan5pin = gpok;
else
- fan5pin = superio_inb(sio_data->sioreg, 0x1C) & 0x02;
+ fan5pin = superio_inb(sioreg, 0x1C) & 0x02;
fan4min = fan4pin;
- fan3min = fan3pin;
+ fan6pin = false;
pwm3pin = fan3pin;
- pwm4pin = 0;
- pwm5pin = 0;
- } else { /* NCT6779D */
- regval = superio_inb(sio_data->sioreg, 0x1c);
+ pwm4pin = false;
+ pwm5pin = false;
+ pwm6pin = false;
+ } else if (data->kind == nct6106) {
+ regval = superio_inb(sioreg, 0x24);
+ fan3pin = !(regval & 0x80);
+ pwm3pin = regval & 0x08;
+
+ fan4pin = false;
+ fan4min = false;
+ fan5pin = false;
+ fan6pin = false;
+ pwm4pin = false;
+ pwm5pin = false;
+ pwm6pin = false;
+ } else { /* NCT6779D or NCT6791D */
+ regval = superio_inb(sioreg, 0x1c);
fan3pin = !(regval & (1 << 5));
fan4pin = !(regval & (1 << 6));
pwm4pin = !(regval & (1 << 1));
pwm5pin = !(regval & (1 << 2));
- fan3min = fan3pin;
fan4min = fan4pin;
- }
-
- superio_exit(sio_data->sioreg);
- data->has_fan = data->has_fan_min = 0x03; /* fan1 and fan2 */
- data->has_fan |= fan3pin << 2;
- data->has_fan_min |= fan3min << 2;
-
- data->has_fan |= (fan4pin << 3) | (fan5pin << 4);
- data->has_fan_min |= (fan4min << 3) | (fan5pin << 4);
-
- data->has_pwm = 0x03 | (pwm3pin << 2) | (pwm4pin << 3) | (pwm5pin << 4);
+ if (data->kind == nct6791) {
+ regval = superio_inb(sioreg, 0x2d);
+ fan6pin = (regval & (1 << 1));
+ pwm6pin = (regval & (1 << 0));
+ } else { /* NCT6779D */
+ fan6pin = false;
+ pwm6pin = false;
+ }
+ }
- return 0;
+ /* fan 1 and 2 (0x03) are always present */
+ data->has_fan = 0x03 | (fan3pin << 2) | (fan4pin << 3) |
+ (fan5pin << 4) | (fan6pin << 5);
+ data->has_fan_min = 0x03 | (fan3pin << 2) | (fan4min << 3) |
+ (fan5pin << 4);
+ data->has_pwm = 0x03 | (pwm3pin << 2) | (pwm4pin << 3) |
+ (pwm5pin << 4) | (pwm6pin << 5);
}
static void add_temp_sensors(struct nct6775_data *data, const u16 *regp,
static int nct6775_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct nct6775_sio_data *sio_data = dev->platform_data;
+ struct nct6775_sio_data *sio_data = dev_get_platdata(dev);
struct nct6775_data *data;
struct resource *res;
int i, s, err = 0;
int src, mask, available;
const u16 *reg_temp, *reg_temp_over, *reg_temp_hyst, *reg_temp_config;
const u16 *reg_temp_alternate, *reg_temp_crit;
+ const u16 *reg_temp_crit_l = NULL, *reg_temp_crit_h = NULL;
int num_reg_temp;
- bool have_vid = false;
u8 cr2a;
+ struct attribute_group *group;
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (!devm_request_region(&pdev->dev, res->start, IOREGION_LENGTH,
return -ENOMEM;
data->kind = sio_data->kind;
+ data->sioreg = sio_data->sioreg;
data->addr = res->start;
mutex_init(&data->update_lock);
data->name = nct6775_device_names[data->kind];
platform_set_drvdata(pdev, data);
switch (data->kind) {
+ case nct6106:
+ data->in_num = 9;
+ data->pwm_num = 3;
+ data->auto_pwm_num = 4;
+ data->temp_fixed_num = 3;
+ data->num_temp_alarms = 6;
+ data->num_temp_beeps = 6;
+
+ data->fan_from_reg = fan_from_reg13;
+ data->fan_from_reg_min = fan_from_reg13;
+
+ data->temp_label = nct6776_temp_label;
+ data->temp_label_num = ARRAY_SIZE(nct6776_temp_label);
+
+ data->REG_VBAT = NCT6106_REG_VBAT;
+ data->REG_DIODE = NCT6106_REG_DIODE;
+ data->DIODE_MASK = NCT6106_DIODE_MASK;
+ data->REG_VIN = NCT6106_REG_IN;
+ data->REG_IN_MINMAX[0] = NCT6106_REG_IN_MIN;
+ data->REG_IN_MINMAX[1] = NCT6106_REG_IN_MAX;
+ data->REG_TARGET = NCT6106_REG_TARGET;
+ data->REG_FAN = NCT6106_REG_FAN;
+ data->REG_FAN_MODE = NCT6106_REG_FAN_MODE;
+ data->REG_FAN_MIN = NCT6106_REG_FAN_MIN;
+ data->REG_FAN_PULSES = NCT6106_REG_FAN_PULSES;
+ data->FAN_PULSE_SHIFT = NCT6106_FAN_PULSE_SHIFT;
+ data->REG_FAN_TIME[0] = NCT6106_REG_FAN_STOP_TIME;
+ data->REG_FAN_TIME[1] = NCT6106_REG_FAN_STEP_UP_TIME;
+ data->REG_FAN_TIME[2] = NCT6106_REG_FAN_STEP_DOWN_TIME;
+ data->REG_PWM[0] = NCT6106_REG_PWM;
+ data->REG_PWM[1] = NCT6106_REG_FAN_START_OUTPUT;
+ data->REG_PWM[2] = NCT6106_REG_FAN_STOP_OUTPUT;
+ data->REG_PWM[5] = NCT6106_REG_WEIGHT_DUTY_STEP;
+ data->REG_PWM[6] = NCT6106_REG_WEIGHT_DUTY_BASE;
+ data->REG_PWM_READ = NCT6106_REG_PWM_READ;
+ data->REG_PWM_MODE = NCT6106_REG_PWM_MODE;
+ data->PWM_MODE_MASK = NCT6106_PWM_MODE_MASK;
+ data->REG_AUTO_TEMP = NCT6106_REG_AUTO_TEMP;
+ data->REG_AUTO_PWM = NCT6106_REG_AUTO_PWM;
+ data->REG_CRITICAL_TEMP = NCT6106_REG_CRITICAL_TEMP;
+ data->REG_CRITICAL_TEMP_TOLERANCE
+ = NCT6106_REG_CRITICAL_TEMP_TOLERANCE;
+ data->REG_CRITICAL_PWM_ENABLE = NCT6106_REG_CRITICAL_PWM_ENABLE;
+ data->CRITICAL_PWM_ENABLE_MASK
+ = NCT6106_CRITICAL_PWM_ENABLE_MASK;
+ data->REG_CRITICAL_PWM = NCT6106_REG_CRITICAL_PWM;
+ data->REG_TEMP_OFFSET = NCT6106_REG_TEMP_OFFSET;
+ data->REG_TEMP_SOURCE = NCT6106_REG_TEMP_SOURCE;
+ data->REG_TEMP_SEL = NCT6106_REG_TEMP_SEL;
+ data->REG_WEIGHT_TEMP_SEL = NCT6106_REG_WEIGHT_TEMP_SEL;
+ data->REG_WEIGHT_TEMP[0] = NCT6106_REG_WEIGHT_TEMP_STEP;
+ data->REG_WEIGHT_TEMP[1] = NCT6106_REG_WEIGHT_TEMP_STEP_TOL;
+ data->REG_WEIGHT_TEMP[2] = NCT6106_REG_WEIGHT_TEMP_BASE;
+ data->REG_ALARM = NCT6106_REG_ALARM;
+ data->ALARM_BITS = NCT6106_ALARM_BITS;
+ data->REG_BEEP = NCT6106_REG_BEEP;
+ data->BEEP_BITS = NCT6106_BEEP_BITS;
+
+ reg_temp = NCT6106_REG_TEMP;
+ num_reg_temp = ARRAY_SIZE(NCT6106_REG_TEMP);
+ reg_temp_over = NCT6106_REG_TEMP_OVER;
+ reg_temp_hyst = NCT6106_REG_TEMP_HYST;
+ reg_temp_config = NCT6106_REG_TEMP_CONFIG;
+ reg_temp_alternate = NCT6106_REG_TEMP_ALTERNATE;
+ reg_temp_crit = NCT6106_REG_TEMP_CRIT;
+ reg_temp_crit_l = NCT6106_REG_TEMP_CRIT_L;
+ reg_temp_crit_h = NCT6106_REG_TEMP_CRIT_H;
+
+ break;
case nct6775:
data->in_num = 9;
data->pwm_num = 3;
data->has_fan_div = true;
data->temp_fixed_num = 3;
data->num_temp_alarms = 3;
+ data->num_temp_beeps = 3;
data->ALARM_BITS = NCT6775_ALARM_BITS;
+ data->BEEP_BITS = NCT6775_BEEP_BITS;
data->fan_from_reg = fan_from_reg16;
data->fan_from_reg_min = fan_from_reg8;
data->REG_CONFIG = NCT6775_REG_CONFIG;
data->REG_VBAT = NCT6775_REG_VBAT;
data->REG_DIODE = NCT6775_REG_DIODE;
+ data->DIODE_MASK = NCT6775_DIODE_MASK;
data->REG_VIN = NCT6775_REG_IN;
data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN;
data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX;
data->REG_FAN_MODE = NCT6775_REG_FAN_MODE;
data->REG_FAN_MIN = NCT6775_REG_FAN_MIN;
data->REG_FAN_PULSES = NCT6775_REG_FAN_PULSES;
+ data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT;
data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME;
data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME;
data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME;
data->REG_WEIGHT_TEMP[1] = NCT6775_REG_WEIGHT_TEMP_STEP_TOL;
data->REG_WEIGHT_TEMP[2] = NCT6775_REG_WEIGHT_TEMP_BASE;
data->REG_ALARM = NCT6775_REG_ALARM;
+ data->REG_BEEP = NCT6775_REG_BEEP;
reg_temp = NCT6775_REG_TEMP;
num_reg_temp = ARRAY_SIZE(NCT6775_REG_TEMP);
data->has_fan_div = false;
data->temp_fixed_num = 3;
data->num_temp_alarms = 3;
+ data->num_temp_beeps = 6;
data->ALARM_BITS = NCT6776_ALARM_BITS;
+ data->BEEP_BITS = NCT6776_BEEP_BITS;
data->fan_from_reg = fan_from_reg13;
data->fan_from_reg_min = fan_from_reg13;
data->REG_CONFIG = NCT6775_REG_CONFIG;
data->REG_VBAT = NCT6775_REG_VBAT;
data->REG_DIODE = NCT6775_REG_DIODE;
+ data->DIODE_MASK = NCT6775_DIODE_MASK;
data->REG_VIN = NCT6775_REG_IN;
data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN;
data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX;
data->REG_FAN_MODE = NCT6775_REG_FAN_MODE;
data->REG_FAN_MIN = NCT6776_REG_FAN_MIN;
data->REG_FAN_PULSES = NCT6776_REG_FAN_PULSES;
+ data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT;
data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME;
data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME;
data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME;
data->REG_WEIGHT_TEMP[1] = NCT6775_REG_WEIGHT_TEMP_STEP_TOL;
data->REG_WEIGHT_TEMP[2] = NCT6775_REG_WEIGHT_TEMP_BASE;
data->REG_ALARM = NCT6775_REG_ALARM;
+ data->REG_BEEP = NCT6776_REG_BEEP;
reg_temp = NCT6775_REG_TEMP;
num_reg_temp = ARRAY_SIZE(NCT6775_REG_TEMP);
data->has_fan_div = false;
data->temp_fixed_num = 6;
data->num_temp_alarms = 2;
+ data->num_temp_beeps = 2;
data->ALARM_BITS = NCT6779_ALARM_BITS;
+ data->BEEP_BITS = NCT6779_BEEP_BITS;
data->fan_from_reg = fan_from_reg13;
data->fan_from_reg_min = fan_from_reg13;
data->REG_CONFIG = NCT6775_REG_CONFIG;
data->REG_VBAT = NCT6775_REG_VBAT;
data->REG_DIODE = NCT6775_REG_DIODE;
+ data->DIODE_MASK = NCT6775_DIODE_MASK;
data->REG_VIN = NCT6779_REG_IN;
data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN;
data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX;
data->REG_FAN_MODE = NCT6775_REG_FAN_MODE;
data->REG_FAN_MIN = NCT6776_REG_FAN_MIN;
data->REG_FAN_PULSES = NCT6779_REG_FAN_PULSES;
+ data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT;
data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME;
data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME;
data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME;
data->REG_CRITICAL_TEMP = NCT6775_REG_CRITICAL_TEMP;
data->REG_CRITICAL_TEMP_TOLERANCE
= NCT6775_REG_CRITICAL_TEMP_TOLERANCE;
+ data->REG_CRITICAL_PWM_ENABLE = NCT6779_REG_CRITICAL_PWM_ENABLE;
+ data->CRITICAL_PWM_ENABLE_MASK
+ = NCT6779_CRITICAL_PWM_ENABLE_MASK;
+ data->REG_CRITICAL_PWM = NCT6779_REG_CRITICAL_PWM;
data->REG_TEMP_OFFSET = NCT6779_REG_TEMP_OFFSET;
data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE;
data->REG_TEMP_SEL = NCT6775_REG_TEMP_SEL;
data->REG_WEIGHT_TEMP[1] = NCT6775_REG_WEIGHT_TEMP_STEP_TOL;
data->REG_WEIGHT_TEMP[2] = NCT6775_REG_WEIGHT_TEMP_BASE;
data->REG_ALARM = NCT6779_REG_ALARM;
+ data->REG_BEEP = NCT6776_REG_BEEP;
+
+ reg_temp = NCT6779_REG_TEMP;
+ num_reg_temp = ARRAY_SIZE(NCT6779_REG_TEMP);
+ reg_temp_over = NCT6779_REG_TEMP_OVER;
+ reg_temp_hyst = NCT6779_REG_TEMP_HYST;
+ reg_temp_config = NCT6779_REG_TEMP_CONFIG;
+ reg_temp_alternate = NCT6779_REG_TEMP_ALTERNATE;
+ reg_temp_crit = NCT6779_REG_TEMP_CRIT;
+
+ break;
+ case nct6791:
+ data->in_num = 15;
+ data->pwm_num = 6;
+ data->auto_pwm_num = 4;
+ data->has_fan_div = false;
+ data->temp_fixed_num = 6;
+ data->num_temp_alarms = 2;
+ data->num_temp_beeps = 2;
+
+ data->ALARM_BITS = NCT6791_ALARM_BITS;
+ data->BEEP_BITS = NCT6779_BEEP_BITS;
+
+ data->fan_from_reg = fan_from_reg13;
+ data->fan_from_reg_min = fan_from_reg13;
+ data->target_temp_mask = 0xff;
+ data->tolerance_mask = 0x07;
+ data->speed_tolerance_limit = 63;
+
+ data->temp_label = nct6779_temp_label;
+ data->temp_label_num = ARRAY_SIZE(nct6779_temp_label);
+
+ data->REG_CONFIG = NCT6775_REG_CONFIG;
+ data->REG_VBAT = NCT6775_REG_VBAT;
+ data->REG_DIODE = NCT6775_REG_DIODE;
+ data->DIODE_MASK = NCT6775_DIODE_MASK;
+ data->REG_VIN = NCT6779_REG_IN;
+ data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN;
+ data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX;
+ data->REG_TARGET = NCT6775_REG_TARGET;
+ data->REG_FAN = NCT6779_REG_FAN;
+ data->REG_FAN_MODE = NCT6775_REG_FAN_MODE;
+ data->REG_FAN_MIN = NCT6776_REG_FAN_MIN;
+ data->REG_FAN_PULSES = NCT6779_REG_FAN_PULSES;
+ data->FAN_PULSE_SHIFT = NCT6775_FAN_PULSE_SHIFT;
+ data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME;
+ data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME;
+ data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME;
+ data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H;
+ data->REG_PWM[0] = NCT6775_REG_PWM;
+ data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT;
+ data->REG_PWM[2] = NCT6775_REG_FAN_STOP_OUTPUT;
+ data->REG_PWM[5] = NCT6775_REG_WEIGHT_DUTY_STEP;
+ data->REG_PWM[6] = NCT6776_REG_WEIGHT_DUTY_BASE;
+ data->REG_PWM_READ = NCT6775_REG_PWM_READ;
+ data->REG_PWM_MODE = NCT6776_REG_PWM_MODE;
+ data->PWM_MODE_MASK = NCT6776_PWM_MODE_MASK;
+ data->REG_AUTO_TEMP = NCT6775_REG_AUTO_TEMP;
+ data->REG_AUTO_PWM = NCT6775_REG_AUTO_PWM;
+ data->REG_CRITICAL_TEMP = NCT6775_REG_CRITICAL_TEMP;
+ data->REG_CRITICAL_TEMP_TOLERANCE
+ = NCT6775_REG_CRITICAL_TEMP_TOLERANCE;
+ data->REG_CRITICAL_PWM_ENABLE = NCT6779_REG_CRITICAL_PWM_ENABLE;
+ data->CRITICAL_PWM_ENABLE_MASK
+ = NCT6779_CRITICAL_PWM_ENABLE_MASK;
+ data->REG_CRITICAL_PWM = NCT6779_REG_CRITICAL_PWM;
+ data->REG_TEMP_OFFSET = NCT6779_REG_TEMP_OFFSET;
+ data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE;
+ data->REG_TEMP_SEL = NCT6775_REG_TEMP_SEL;
+ data->REG_WEIGHT_TEMP_SEL = NCT6775_REG_WEIGHT_TEMP_SEL;
+ data->REG_WEIGHT_TEMP[0] = NCT6775_REG_WEIGHT_TEMP_STEP;
+ data->REG_WEIGHT_TEMP[1] = NCT6775_REG_WEIGHT_TEMP_STEP_TOL;
+ data->REG_WEIGHT_TEMP[2] = NCT6775_REG_WEIGHT_TEMP_BASE;
+ data->REG_ALARM = NCT6791_REG_ALARM;
+ data->REG_BEEP = NCT6776_REG_BEEP;
reg_temp = NCT6779_REG_TEMP;
num_reg_temp = ARRAY_SIZE(NCT6779_REG_TEMP);
data->reg_temp[0][src - 1] = reg_temp[i];
data->reg_temp[1][src - 1] = reg_temp_over[i];
data->reg_temp[2][src - 1] = reg_temp_hyst[i];
+ if (reg_temp_crit_h && reg_temp_crit_h[i])
+ data->reg_temp[3][src - 1] = reg_temp_crit_h[i];
+ else if (reg_temp_crit[src - 1])
+ data->reg_temp[3][src - 1]
+ = reg_temp_crit[src - 1];
+ if (reg_temp_crit_l && reg_temp_crit_l[i])
+ data->reg_temp[4][src - 1] = reg_temp_crit_l[i];
data->reg_temp_config[src - 1] = reg_temp_config[i];
data->temp_src[src - 1] = src;
continue;
data->reg_temp[1][s] = reg_temp_over[i];
data->reg_temp[2][s] = reg_temp_hyst[i];
data->reg_temp_config[s] = reg_temp_config[i];
- if (reg_temp_crit[src - 1])
+ if (reg_temp_crit_h && reg_temp_crit_h[i])
+ data->reg_temp[3][s] = reg_temp_crit_h[i];
+ else if (reg_temp_crit[src - 1])
data->reg_temp[3][s] = reg_temp_crit[src - 1];
+ if (reg_temp_crit_l && reg_temp_crit_l[i])
+ data->reg_temp[4][s] = reg_temp_crit_l[i];
data->temp_src[s] = src;
s++;
cr2a = superio_inb(sio_data->sioreg, 0x2a);
switch (data->kind) {
case nct6775:
- have_vid = (cr2a & 0x40);
+ data->have_vid = (cr2a & 0x40);
break;
case nct6776:
- have_vid = (cr2a & 0x60) == 0x40;
+ data->have_vid = (cr2a & 0x60) == 0x40;
break;
+ case nct6106:
case nct6779:
+ case nct6791:
break;
}
* Read VID value
* We can get the VID input values directly at logical device D 0xe3.
*/
- if (have_vid) {
+ if (data->have_vid) {
superio_select(sio_data->sioreg, NCT6775_LD_VID);
data->vid = superio_inb(sio_data->sioreg, 0xe3);
data->vrm = vid_which_vrm();
tmp = superio_inb(sio_data->sioreg,
NCT6775_REG_CR_FAN_DEBOUNCE);
switch (data->kind) {
+ case nct6106:
+ tmp |= 0xe0;
+ break;
case nct6775:
tmp |= 0x1e;
break;
case nct6779:
tmp |= 0x3e;
break;
+ case nct6791:
+ tmp |= 0x7e;
+ break;
}
superio_outb(sio_data->sioreg, NCT6775_REG_CR_FAN_DEBOUNCE,
tmp);
data->name);
}
- superio_exit(sio_data->sioreg);
-
- if (have_vid) {
- err = device_create_file(dev, &dev_attr_cpu0_vid);
- if (err)
- return err;
- }
+ nct6775_check_fan_inputs(data);
- err = nct6775_check_fan_inputs(sio_data, data);
- if (err)
- goto exit_remove;
+ superio_exit(sio_data->sioreg);
/* Read fan clock dividers immediately */
nct6775_init_fan_common(dev, data);
/* Register sysfs hooks */
- for (i = 0; i < data->pwm_num; i++) {
- if (!(data->has_pwm & (1 << i)))
- continue;
-
- err = sysfs_create_group(&dev->kobj, &nct6775_group_pwm[i]);
- if (err)
- goto exit_remove;
-
- if (data->REG_PWM[3]) {
- err = device_create_file(dev,
- &sda_pwm_max[i].dev_attr);
- if (err)
- goto exit_remove;
- }
- if (data->REG_PWM[4]) {
- err = device_create_file(dev,
- &sda_pwm_step[i].dev_attr);
- if (err)
- goto exit_remove;
- }
- if (data->REG_PWM[6]) {
- err = device_create_file(dev,
- &sda_weight_duty_base[i].dev_attr);
- if (err)
- goto exit_remove;
- }
- }
- for (i = 0; i < ARRAY_SIZE(sda_auto_pwm_arrays); i++) {
- struct sensor_device_attribute_2 *attr =
- &sda_auto_pwm_arrays[i];
-
- if (!(data->has_pwm & (1 << attr->nr)))
- continue;
- if (attr->index > data->auto_pwm_num)
- continue;
- err = device_create_file(dev, &attr->dev_attr);
- if (err)
- goto exit_remove;
- }
-
- for (i = 0; i < data->in_num; i++) {
- if (!(data->have_in & (1 << i)))
- continue;
- err = sysfs_create_group(&dev->kobj, &nct6775_group_in[i]);
- if (err)
- goto exit_remove;
+ group = nct6775_create_attr_group(dev, &nct6775_pwm_template_group,
+ data->pwm_num);
+ if (IS_ERR(group)) {
+ err = PTR_ERR(group);
+ goto exit_remove;
}
+ data->group_pwm = group;
- for (i = 0; i < 5; i++) {
- if (data->has_fan & (1 << i)) {
- err = device_create_file(dev,
- &sda_fan_input[i].dev_attr);
- if (err)
- goto exit_remove;
- if (data->ALARM_BITS[FAN_ALARM_BASE + i] >= 0) {
- err = device_create_file(dev,
- &sda_fan_alarm[i].dev_attr);
- if (err)
- goto exit_remove;
- }
- if (data->kind != nct6776 &&
- data->kind != nct6779) {
- err = device_create_file(dev,
- &sda_fan_div[i].dev_attr);
- if (err)
- goto exit_remove;
- }
- if (data->has_fan_min & (1 << i)) {
- err = device_create_file(dev,
- &sda_fan_min[i].dev_attr);
- if (err)
- goto exit_remove;
- }
- err = device_create_file(dev,
- &sda_fan_pulses[i].dev_attr);
- if (err)
- goto exit_remove;
- }
+ group = nct6775_create_attr_group(dev, &nct6775_in_template_group,
+ fls(data->have_in));
+ if (IS_ERR(group)) {
+ err = PTR_ERR(group);
+ goto exit_remove;
}
+ data->group_in = group;
- for (i = 0; i < NUM_TEMP; i++) {
- if (!(data->have_temp & (1 << i)))
- continue;
- err = device_create_file(dev, &sda_temp_input[i].dev_attr);
- if (err)
- goto exit_remove;
- if (data->temp_label) {
- err = device_create_file(dev,
- &sda_temp_label[i].dev_attr);
- if (err)
- goto exit_remove;
- }
- if (data->reg_temp[1][i]) {
- err = device_create_file(dev,
- &sda_temp_max[i].dev_attr);
- if (err)
- goto exit_remove;
- }
- if (data->reg_temp[2][i]) {
- err = device_create_file(dev,
- &sda_temp_max_hyst[i].dev_attr);
- if (err)
- goto exit_remove;
- }
- if (data->reg_temp[3][i]) {
- err = device_create_file(dev,
- &sda_temp_crit[i].dev_attr);
- if (err)
- goto exit_remove;
- }
- if (find_temp_source(data, i, data->num_temp_alarms) >= 0) {
- err = device_create_file(dev,
- &sda_temp_alarm[i].dev_attr);
- if (err)
- goto exit_remove;
- }
- if (!(data->have_temp_fixed & (1 << i)))
- continue;
- err = device_create_file(dev, &sda_temp_type[i].dev_attr);
- if (err)
- goto exit_remove;
- err = device_create_file(dev, &sda_temp_offset[i].dev_attr);
- if (err)
- goto exit_remove;
+ group = nct6775_create_attr_group(dev, &nct6775_fan_template_group,
+ fls(data->has_fan));
+ if (IS_ERR(group)) {
+ err = PTR_ERR(group);
+ goto exit_remove;
}
+ data->group_fan = group;
- for (i = 0; i < ARRAY_SIZE(sda_caseopen); i++) {
- if (data->ALARM_BITS[INTRUSION_ALARM_BASE + i] < 0)
- continue;
- err = device_create_file(dev, &sda_caseopen[i].dev_attr);
- if (err)
- goto exit_remove;
+ group = nct6775_create_attr_group(dev, &nct6775_temp_template_group,
+ fls(data->have_temp));
+ if (IS_ERR(group)) {
+ err = PTR_ERR(group);
+ goto exit_remove;
}
+ data->group_temp = group;
- err = device_create_file(dev, &dev_attr_name);
+ err = sysfs_create_group(&dev->kobj, &nct6775_group_other);
if (err)
goto exit_remove;
static int nct6775_suspend(struct device *dev)
{
struct nct6775_data *data = nct6775_update_device(dev);
- struct nct6775_sio_data *sio_data = dev->platform_data;
mutex_lock(&data->update_lock);
data->vbat = nct6775_read_value(data, data->REG_VBAT);
- if (sio_data->kind == nct6775) {
+ if (data->kind == nct6775) {
data->fandiv1 = nct6775_read_value(data, NCT6775_REG_FANDIV1);
data->fandiv2 = nct6775_read_value(data, NCT6775_REG_FANDIV2);
}
static int nct6775_resume(struct device *dev)
{
struct nct6775_data *data = dev_get_drvdata(dev);
- struct nct6775_sio_data *sio_data = dev->platform_data;
int i, j;
mutex_lock(&data->update_lock);
/* Restore other settings */
nct6775_write_value(data, data->REG_VBAT, data->vbat);
- if (sio_data->kind == nct6775) {
+ if (data->kind == nct6775) {
nct6775_write_value(data, NCT6775_REG_FANDIV1, data->fandiv1);
nct6775_write_value(data, NCT6775_REG_FANDIV2, data->fandiv2);
}
static const struct dev_pm_ops nct6775_dev_pm_ops = {
.suspend = nct6775_suspend,
.resume = nct6775_resume,
+ .freeze = nct6775_suspend,
+ .restore = nct6775_resume,
};
#define NCT6775_DEV_PM_OPS (&nct6775_dev_pm_ops)
};
static const char * const nct6775_sio_names[] __initconst = {
+ "NCT6106D",
"NCT6775F",
"NCT6776D/F",
"NCT6779D",
+ "NCT6791D",
};
/* nct6775_find() looks for a '627 in the Super-I/O config space */
-static int __init nct6775_find(int sioaddr, unsigned short *addr,
- struct nct6775_sio_data *sio_data)
+static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data)
{
u16 val;
int err;
+ int addr;
err = superio_enter(sioaddr);
if (err)
val = (superio_inb(sioaddr, SIO_REG_DEVID) << 8)
| superio_inb(sioaddr, SIO_REG_DEVID + 1);
switch (val & SIO_ID_MASK) {
+ case SIO_NCT6106_ID:
+ sio_data->kind = nct6106;
+ break;
case SIO_NCT6775_ID:
sio_data->kind = nct6775;
break;
case SIO_NCT6779_ID:
sio_data->kind = nct6779;
break;
+ case SIO_NCT6791_ID:
+ sio_data->kind = nct6791;
+ break;
default:
if (val != 0xffff)
pr_debug("unsupported chip ID: 0x%04x\n", val);
superio_select(sioaddr, NCT6775_LD_HWM);
val = (superio_inb(sioaddr, SIO_REG_ADDR) << 8)
| superio_inb(sioaddr, SIO_REG_ADDR + 1);
- *addr = val & IOREGION_ALIGNMENT;
- if (*addr == 0) {
+ addr = val & IOREGION_ALIGNMENT;
+ if (addr == 0) {
pr_err("Refusing to enable a Super-I/O device with a base I/O port 0\n");
superio_exit(sioaddr);
return -ENODEV;
pr_warn("Forcibly enabling Super-I/O. Sensor is probably unusable.\n");
superio_outb(sioaddr, SIO_REG_ENABLE, val | 0x01);
}
+ if (sio_data->kind == nct6791) {
+ val = superio_inb(sioaddr, NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE);
+ if (val & 0x10) {
+ pr_info("Enabling hardware monitor logical device mappings.\n");
+ superio_outb(sioaddr,
+ NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE,
+ val & ~0x10);
+ }
+ }
superio_exit(sioaddr);
- pr_info("Found %s or compatible chip at %#x\n",
- nct6775_sio_names[sio_data->kind], *addr);
+ pr_info("Found %s or compatible chip at %#x:%#x\n",
+ nct6775_sio_names[sio_data->kind], sioaddr, addr);
sio_data->sioreg = sioaddr;
- return 0;
+ return addr;
}
/*
* track of the nct6775 driver. But since we platform_device_alloc(), we
* must keep track of the device
*/
-static struct platform_device *pdev;
+static struct platform_device *pdev[2];
static int __init sensors_nct6775_init(void)
{
- int err;
- unsigned short address;
+ int i, err;
+ bool found = false;
+ int address;
struct resource res;
struct nct6775_sio_data sio_data;
+ int sioaddr[2] = { 0x2e, 0x4e };
+
+ err = platform_driver_register(&nct6775_driver);
+ if (err)
+ return err;
/*
* initialize sio_data->kind and sio_data->sioreg.
* driver will probe 0x2e and 0x4e and auto-detect the presence of a
* nct6775 hardware monitor, and call probe()
*/
- if (nct6775_find(0x2e, &address, &sio_data) &&
- nct6775_find(0x4e, &address, &sio_data))
- return -ENODEV;
-
- err = platform_driver_register(&nct6775_driver);
- if (err)
- goto exit;
+ for (i = 0; i < ARRAY_SIZE(pdev); i++) {
+ address = nct6775_find(sioaddr[i], &sio_data);
+ if (address <= 0)
+ continue;
- pdev = platform_device_alloc(DRVNAME, address);
- if (!pdev) {
- err = -ENOMEM;
- pr_err("Device allocation failed\n");
- goto exit_unregister;
- }
+ found = true;
- err = platform_device_add_data(pdev, &sio_data,
- sizeof(struct nct6775_sio_data));
- if (err) {
- pr_err("Platform data allocation failed\n");
- goto exit_device_put;
- }
+ pdev[i] = platform_device_alloc(DRVNAME, address);
+ if (!pdev[i]) {
+ err = -ENOMEM;
+ goto exit_device_put;
+ }
- memset(&res, 0, sizeof(res));
- res.name = DRVNAME;
- res.start = address + IOREGION_OFFSET;
- res.end = address + IOREGION_OFFSET + IOREGION_LENGTH - 1;
- res.flags = IORESOURCE_IO;
+ err = platform_device_add_data(pdev[i], &sio_data,
+ sizeof(struct nct6775_sio_data));
+ if (err)
+ goto exit_device_put;
+
+ memset(&res, 0, sizeof(res));
+ res.name = DRVNAME;
+ res.start = address + IOREGION_OFFSET;
+ res.end = address + IOREGION_OFFSET + IOREGION_LENGTH - 1;
+ res.flags = IORESOURCE_IO;
+
+ err = acpi_check_resource_conflict(&res);
+ if (err) {
+ platform_device_put(pdev[i]);
+ pdev[i] = NULL;
+ continue;
+ }
- err = acpi_check_resource_conflict(&res);
- if (err)
- goto exit_device_put;
+ err = platform_device_add_resources(pdev[i], &res, 1);
+ if (err)
+ goto exit_device_put;
- err = platform_device_add_resources(pdev, &res, 1);
- if (err) {
- pr_err("Device resource addition failed (%d)\n", err);
- goto exit_device_put;
+ /* platform_device_add calls probe() */
+ err = platform_device_add(pdev[i]);
+ if (err)
+ goto exit_device_put;
}
-
- /* platform_device_add calls probe() */
- err = platform_device_add(pdev);
- if (err) {
- pr_err("Device addition failed (%d)\n", err);
- goto exit_device_put;
+ if (!found) {
+ err = -ENODEV;
+ goto exit_unregister;
}
return 0;
exit_device_put:
- platform_device_put(pdev);
+ for (i = 0; i < ARRAY_SIZE(pdev); i++) {
+ if (pdev[i])
+ platform_device_put(pdev[i]);
+ }
exit_unregister:
platform_driver_unregister(&nct6775_driver);
-exit:
return err;
}
static void __exit sensors_nct6775_exit(void)
{
- platform_device_unregister(pdev);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pdev); i++) {
+ if (pdev[i])
+ platform_device_unregister(pdev[i]);
+ }
platform_driver_unregister(&nct6775_driver);
}
if (IS_ERR(pdata))
return PTR_ERR(pdata);
else if (pdata == NULL)
- pdata = pdev->dev.platform_data;
+ pdata = dev_get_platdata(&pdev->dev);
if (!pdata) {
dev_err(&pdev->dev, "No platform init data supplied.\n");
static void pc87427_init_device(struct device *dev)
{
- struct pc87427_sio_data *sio_data = dev->platform_data;
+ struct pc87427_sio_data *sio_data = dev_get_platdata(dev);
struct pc87427_data *data = dev_get_drvdata(dev);
int i;
u8 reg;
static int pc87427_probe(struct platform_device *pdev)
{
- struct pc87427_sio_data *sio_data = pdev->dev.platform_data;
+ struct pc87427_sio_data *sio_data = dev_get_platdata(&pdev->dev);
struct pc87427_data *data;
int i, err, res_count;
struct pmbus_driver_info *info)
{
struct device *dev = &client->dev;
- const struct pmbus_platform_data *pdata = dev->platform_data;
+ const struct pmbus_platform_data *pdata = dev_get_platdata(dev);
struct pmbus_data *data;
int ret;
{
struct sensor_device_attribute *sen_attr = to_sensor_dev_attr(attr);
struct s3c_hwmon *hwmon = platform_get_drvdata(to_platform_device(dev));
- struct s3c_hwmon_pdata *pdata = dev->platform_data;
+ struct s3c_hwmon_pdata *pdata = dev_get_platdata(dev);
struct s3c_hwmon_chcfg *cfg;
int ret;
char *buf)
{
struct sensor_device_attribute *sen_attr = to_sensor_dev_attr(attr);
- struct s3c_hwmon_pdata *pdata = dev->platform_data;
+ struct s3c_hwmon_pdata *pdata = dev_get_platdata(dev);
struct s3c_hwmon_chcfg *cfg;
cfg = pdata->in[sen_attr->index];
*/
static int s3c_hwmon_probe(struct platform_device *dev)
{
- struct s3c_hwmon_pdata *pdata = dev->dev.platform_data;
+ struct s3c_hwmon_pdata *pdata = dev_get_platdata(&dev->dev);
struct s3c_hwmon *hwmon;
int ret = 0;
int i;
data->dev = &pdev->dev;
init_waitqueue_head(&data->wait_queue);
- if (pdev->dev.platform_data == NULL) {
+ if (dev_get_platdata(&pdev->dev) == NULL) {
dev_err(&pdev->dev, "no platform data supplied\n");
return -EINVAL;
}
- data->pdata = pdev->dev.platform_data;
+ data->pdata = dev_get_platdata(&pdev->dev);
data->supply_uv = data->pdata->supply_mv * 1000;
if (data->pdata->checksum)
data->checksumming = true;
* If a regulator is available,
* query what the supply voltage actually is!
*/
- data->reg = devm_regulator_get(data->dev, "vcc");
+ data->reg = devm_regulator_get_optional(data->dev, "vcc");
if (!IS_ERR(data->reg)) {
int voltage;
static int __init smsc47m1_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct smsc47m1_sio_data *sio_data = dev->platform_data;
+ struct smsc47m1_sio_data *sio_data = dev_get_platdata(dev);
struct smsc47m1_data *data;
struct resource *res;
int err;
static void __exit sm_smsc47m1_exit(void)
{
platform_driver_unregister(&smsc47m1_driver);
- smsc47m1_restore(pdev->dev.platform_data);
+ smsc47m1_restore(dev_get_platdata(&pdev->dev));
platform_device_unregister(pdev);
}
static void w83627ehf_write_fan_div_common(struct device *dev,
struct w83627ehf_data *data, int nr)
{
- struct w83627ehf_sio_data *sio_data = dev->platform_data;
+ struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev);
if (sio_data->kind == nct6776)
; /* no dividers, do nothing */
static void w83627ehf_update_fan_div_common(struct device *dev,
struct w83627ehf_data *data)
{
- struct w83627ehf_sio_data *sio_data = dev->platform_data;
+ struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev);
if (sio_data->kind == nct6776)
; /* no dividers, do nothing */
static void w83627ehf_update_pwm_common(struct device *dev,
struct w83627ehf_data *data)
{
- struct w83627ehf_sio_data *sio_data = dev->platform_data;
+ struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev);
if (sio_data->kind == nct6775 || sio_data->kind == nct6776)
nct6775_update_pwm(data);
static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
{
struct w83627ehf_data *data = dev_get_drvdata(dev);
- struct w83627ehf_sio_data *sio_data = dev->platform_data;
+ struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev);
int i;
{
struct w83627ehf_data *data = dev_get_drvdata(dev);
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
- struct w83627ehf_sio_data *sio_data = dev->platform_data;
+ struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev);
int nr = sensor_attr->index;
unsigned long val;
int err;
const char *buf, size_t count)
{
struct w83627ehf_data *data = dev_get_drvdata(dev);
- struct w83627ehf_sio_data *sio_data = dev->platform_data;
+ struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev);
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
int nr = sensor_attr->index;
unsigned long val;
const char *buf, size_t count)
{
struct w83627ehf_data *data = dev_get_drvdata(dev);
- struct w83627ehf_sio_data *sio_data = dev->platform_data;
+ struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev);
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
int nr = sensor_attr->index;
u16 reg;
static int w83627ehf_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct w83627ehf_sio_data *sio_data = dev->platform_data;
+ struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev);
struct w83627ehf_data *data;
struct resource *res;
u8 en_vrm10;
static int w83627ehf_suspend(struct device *dev)
{
struct w83627ehf_data *data = w83627ehf_update_device(dev);
- struct w83627ehf_sio_data *sio_data = dev->platform_data;
+ struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev);
mutex_lock(&data->update_lock);
data->vbat = w83627ehf_read_value(data, W83627EHF_REG_VBAT);
static int w83627ehf_resume(struct device *dev)
{
struct w83627ehf_data *data = dev_get_drvdata(dev);
- struct w83627ehf_sio_data *sio_data = dev->platform_data;
+ struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev);
int i;
mutex_lock(&data->update_lock);
static const struct dev_pm_ops w83627ehf_dev_pm_ops = {
.suspend = w83627ehf_suspend,
.resume = w83627ehf_resume,
+ .freeze = w83627ehf_suspend,
+ .restore = w83627ehf_resume,
};
#define W83627EHF_DEV_PM_OPS (&w83627ehf_dev_pm_ops)
static int w83627hf_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct w83627hf_sio_data *sio_data = dev->platform_data;
+ struct w83627hf_sio_data *sio_data = dev_get_platdata(dev);
struct w83627hf_data *data;
struct resource *res;
int err, i;
static int w83627thf_read_gpio5(struct platform_device *pdev)
{
- struct w83627hf_sio_data *sio_data = pdev->dev.platform_data;
+ struct w83627hf_sio_data *sio_data = dev_get_platdata(&pdev->dev);
int res = 0xff, sel;
superio_enter(sio_data);
static int w83687thf_read_vid(struct platform_device *pdev)
{
- struct w83627hf_sio_data *sio_data = pdev->dev.platform_data;
+ struct w83627hf_sio_data *sio_data = dev_get_platdata(&pdev->dev);
int res = 0xff;
superio_enter(sio_data);
switch (mask) {
case IIO_CHAN_INFO_RAW:
- ret = adjd_s311_read_data(indio_dev, chan->address, val);
+ ret = adjd_s311_read_data(indio_dev,
+ ADJD_S311_DATA_REG(chan->address), val);
if (ret < 0)
return ret;
return IIO_VAL_INT;
{ 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
{ 0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller", 0, XTYPE_XBOX360 },
{ 0x1689, 0xfd00, "Razer Onza Tournament Edition", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
+ { 0x1689, 0xfd01, "Razer Onza Classic Edition", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
{ 0x1bad, 0x0002, "Harmonix Rock Band Guitar", 0, XTYPE_XBOX360 },
{ 0x1bad, 0x0003, "Harmonix Rock Band Drumkit", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
{ 0x1bad, 0xf016, "Mad Catz Xbox 360 Controller", 0, XTYPE_XBOX360 },
*/
static int elantech_packet_check_v3(struct psmouse *psmouse)
{
+ struct elantech_data *etd = psmouse->private;
const u8 debounce_packet[] = { 0xc4, 0xff, 0xff, 0x02, 0xff, 0xff };
unsigned char *packet = psmouse->packet;
if (!memcmp(packet, debounce_packet, sizeof(debounce_packet)))
return PACKET_DEBOUNCE;
- if ((packet[0] & 0x0c) == 0x04 && (packet[3] & 0xcf) == 0x02)
- return PACKET_V3_HEAD;
+ /*
+ * If the hardware flag 'crc_enabled' is set the packets have
+ * different signatures.
+ */
+ if (etd->crc_enabled) {
+ if ((packet[3] & 0x09) == 0x08)
+ return PACKET_V3_HEAD;
+
+ if ((packet[3] & 0x09) == 0x09)
+ return PACKET_V3_TAIL;
+ } else {
+ if ((packet[0] & 0x0c) == 0x04 && (packet[3] & 0xcf) == 0x02)
+ return PACKET_V3_HEAD;
- if ((packet[0] & 0x0c) == 0x0c && (packet[3] & 0xce) == 0x0c)
- return PACKET_V3_TAIL;
+ if ((packet[0] & 0x0c) == 0x0c && (packet[3] & 0xce) == 0x0c)
+ return PACKET_V3_TAIL;
+ }
return PACKET_UNKNOWN;
}
static int elantech_packet_check_v4(struct psmouse *psmouse)
{
+ struct elantech_data *etd = psmouse->private;
unsigned char *packet = psmouse->packet;
unsigned char packet_type = packet[3] & 0x03;
+ bool sanity_check;
+
+ /*
+ * Sanity check based on the constant bits of a packet.
+ * The constant bits change depending on the value of
+ * the hardware flag 'crc_enabled' but are the same for
+ * every packet, regardless of the type.
+ */
+ if (etd->crc_enabled)
+ sanity_check = ((packet[3] & 0x08) == 0x00);
+ else
+ sanity_check = ((packet[0] & 0x0c) == 0x04 &&
+ (packet[3] & 0x1c) == 0x10);
+
+ if (!sanity_check)
+ return PACKET_UNKNOWN;
switch (packet_type) {
case 0:
etd->reports_pressure = true;
}
+ /*
+ * The signatures of v3 and v4 packets change depending on the
+ * value of this hardware flag.
+ */
+ etd->crc_enabled = ((etd->fw_version & 0x4000) == 0x4000);
+
return 0;
}
bool paritycheck;
bool jumpy_cursor;
bool reports_pressure;
+ bool crc_enabled;
unsigned char hw_version;
unsigned int fw_version;
unsigned int single_finger_reports;
tristate "i8042 PC Keyboard controller" if EXPERT || !X86
default y
depends on !PARISC && (!ARM || ARCH_SHARK || FOOTBRIDGE_HOST) && \
- (!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN && !S390
+ (!SUPERH || SH_CAYMAN) && !M68K && !BLACKFIN && !S390 && \
+ !ARC
help
i8042 is the chip over which the standard AT keyboard and PS/2
mouse are connected to the computer. If you use these devices,
{ "Wacom Bamboo 2FG 4x5 SE", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023,
31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
.touch_max = 2 };
-static struct wacom_features wacom_features_0xDB =
+static const struct wacom_features wacom_features_0xDB =
{ "Wacom Bamboo 2FG 6x8 SE", WACOM_PKGLEN_BBFUN, 21648, 13700, 1023,
31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
.touch_max = 2 };
{ "Wacom Bamboo 16FG 6x8", WACOM_PKGLEN_BBPEN, 21648, 13700, 1023,
31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
.touch_max = 16 };
+static const struct wacom_features wacom_features_0x300 =
+ { "Wacom Bamboo One S", WACOM_PKGLEN_BBPEN, 14720, 9225, 1023,
+ 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
+static const struct wacom_features wacom_features_0x301 =
+ { "Wacom Bamboo One M", WACOM_PKGLEN_BBPEN, 21648, 13530, 1023,
+ 31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static const struct wacom_features wacom_features_0x6004 =
{ "ISD-V4", WACOM_PKGLEN_GRAPHIRE, 12800, 8000, 255,
0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
{ USB_DEVICE_WACOM(0x100) },
{ USB_DEVICE_WACOM(0x101) },
{ USB_DEVICE_WACOM(0x10D) },
+ { USB_DEVICE_WACOM(0x300) },
+ { USB_DEVICE_WACOM(0x301) },
{ USB_DEVICE_WACOM(0x304) },
{ USB_DEVICE_WACOM(0x4001) },
{ USB_DEVICE_WACOM(0x47) },
#define SIRFSOC_INT_RISC_LEVEL1 0x0024
#define SIRFSOC_INIT_IRQ_ID 0x0038
-#define SIRFSOC_NUM_IRQS 128
+#define SIRFSOC_NUM_IRQS 64
static struct irq_domain *sirfsoc_irqdomain;
{
struct irq_chip_generic *gc;
struct irq_chip_type *ct;
+ int ret;
+ unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
- gc = irq_alloc_generic_chip("SIRFINTC", 1, irq_start, base, handle_level_irq);
- ct = gc->chip_types;
+ ret = irq_alloc_domain_generic_chips(sirfsoc_irqdomain, num, 1, "irq_sirfsoc",
+ handle_level_irq, clr, 0, IRQ_GC_INIT_MASK_CACHE);
+ gc = irq_get_domain_generic_chip(sirfsoc_irqdomain, irq_start);
+ gc->reg_base = base;
+ ct = gc->chip_types;
ct->chip.irq_mask = irq_gc_mask_clr_bit;
ct->chip.irq_unmask = irq_gc_mask_set_bit;
ct->regs.mask = SIRFSOC_INT_RISC_MASK0;
-
- irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE, IRQ_NOREQUEST, 0);
}
static asmlinkage void __exception_irq_entry sirfsoc_handle_irq(struct pt_regs *regs)
if (!base)
panic("unable to map intc cpu registers\n");
- /* using legacy because irqchip_generic does not work with linear */
- sirfsoc_irqdomain = irq_domain_add_legacy(np, SIRFSOC_NUM_IRQS, 0, 0,
- &irq_domain_simple_ops, base);
+ sirfsoc_irqdomain = irq_domain_add_linear(np, SIRFSOC_NUM_IRQS,
+ &irq_generic_chip_ops, base);
sirfsoc_alloc_gc(base, 0, 32);
sirfsoc_alloc_gc(base + 4, 32, SIRFSOC_NUM_IRQS - 32);
u8 *data;
int len;
- if (skb->len < sizeof(int))
+ if (skb->len < sizeof(int)) {
printk(KERN_ERR "%s: PH_CONTROL message too short\n", __func__);
+ return -EINVAL;
+ }
cont = *((int *)skb->data);
len = skb->len - sizeof(int);
data = skb->data + sizeof(int);
return r;
}
-static void remove_mapping(struct mq_policy *mq, dm_oblock_t oblock)
+static void mq_remove_mapping(struct dm_cache_policy *p, dm_oblock_t oblock)
{
- struct entry *e = hash_lookup(mq, oblock);
+ struct mq_policy *mq = to_mq_policy(p);
+ struct entry *e;
+
+ mutex_lock(&mq->lock);
+
+ e = hash_lookup(mq, oblock);
BUG_ON(!e || !e->in_cache);
del(mq, e);
e->in_cache = false;
push(mq, e);
-}
-static void mq_remove_mapping(struct dm_cache_policy *p, dm_oblock_t oblock)
-{
- struct mq_policy *mq = to_mq_policy(p);
-
- mutex_lock(&mq->lock);
- remove_mapping(mq, oblock);
mutex_unlock(&mq->lock);
}
* A driver for the Integrated Circuits ICS932S401
* Copyright (C) 2008 IBM
*
- * Author: Darrick J. Wong <djwong@us.ibm.com>
+ * Author: Darrick J. Wong <darrick.wong@oracle.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
module_i2c_driver(ics932s401_driver);
-MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>");
+MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>");
MODULE_DESCRIPTION("ICS932S401 driver");
MODULE_LICENSE("GPL");
supply = devm_regulator_get(dev, "vmmc");
mmc->supply.vmmc = supply;
- mmc->supply.vqmmc = devm_regulator_get(dev, "vqmmc");
+ mmc->supply.vqmmc = devm_regulator_get_optional(dev, "vqmmc");
if (IS_ERR(supply))
return PTR_ERR(supply);
}
}
- host->vmmc = devm_regulator_get(host->dev, "vmmc");
+ host->vmmc = devm_regulator_get_optional(host->dev, "vmmc");
if (IS_ERR(host->vmmc)) {
ret = PTR_ERR(host->vmmc);
if (ret == -EPROBE_DEFER)
static inline void pxamci_init_ocr(struct pxamci_host *host)
{
#ifdef CONFIG_REGULATOR
- host->vcc = regulator_get(mmc_dev(host->mmc), "vmmc");
+ host->vcc = regulator_get_optional(mmc_dev(host->mmc), "vmmc");
if (IS_ERR(host->vcc))
host->vcc = NULL;
mmc->caps |= MMC_CAP_NEEDS_POLL;
/* If vqmmc regulator and no 1.8V signalling, then there's no UHS */
- host->vqmmc = regulator_get(mmc_dev(mmc), "vqmmc");
+ host->vqmmc = regulator_get_optional(mmc_dev(mmc), "vqmmc");
if (IS_ERR_OR_NULL(host->vqmmc)) {
if (PTR_ERR(host->vqmmc) < 0) {
pr_info("%s: no vqmmc regulator found\n",
ocr_avail = 0;
- host->vmmc = regulator_get(mmc_dev(mmc), "vmmc");
+ host->vmmc = regulator_get_optional(mmc_dev(mmc), "vmmc");
if (IS_ERR_OR_NULL(host->vmmc)) {
if (PTR_ERR(host->vmmc) < 0) {
pr_info("%s: no vmmc regulator found\n",
BNX2X_SP_RTNL_VFPF_CHANNEL_DOWN,
BNX2X_SP_RTNL_VFPF_STORM_RX_MODE,
BNX2X_SP_RTNL_HYPERVISOR_VLAN,
+ BNX2X_SP_RTNL_TX_STOP,
+ BNX2X_SP_RTNL_TX_RESUME,
};
struct bnx2x_prev_path_list {
struct bnx2x_fp_stats *to_fp_stats = &bp->fp_stats[to];
int old_max_eth_txqs, new_max_eth_txqs;
int old_txdata_index = 0, new_txdata_index = 0;
+ struct bnx2x_agg_info *old_tpa_info = to_fp->tpa_info;
/* Copy the NAPI object as it has been already initialized */
from_fp->napi = to_fp->napi;
memcpy(to_fp, from_fp, sizeof(*to_fp));
to_fp->index = to;
+ /* Retain the tpa_info of the original `to' version as we don't want
+ * 2 FPs to contain the same tpa_info pointer.
+ */
+ to_fp->tpa_info = old_tpa_info;
+
/* move sp_objs contents as well, as their indices match fp ones */
memcpy(to_sp_objs, from_sp_objs, sizeof(*to_sp_objs));
if (IS_PF(bp)) {
if (CNIC_LOADED(bp))
bnx2x_free_mem_cnic(bp);
- bnx2x_free_mem(bp);
}
+ bnx2x_free_mem(bp);
+
bp->state = BNX2X_STATE_CLOSED;
bp->cnic_loaded = false;
#include "bnx2x_dcb.h"
/* forward declarations of dcbx related functions */
-static int bnx2x_dcbx_stop_hw_tx(struct bnx2x *bp);
static void bnx2x_pfc_set_pfc(struct bnx2x *bp);
static void bnx2x_dcbx_update_ets_params(struct bnx2x *bp);
-static int bnx2x_dcbx_resume_hw_tx(struct bnx2x *bp);
static void bnx2x_dcbx_get_ets_pri_pg_tbl(struct bnx2x *bp,
u32 *set_configuration_ets_pg,
u32 *pri_pg_tbl);
bnx2x_pfc_clear(bp);
}
-static int bnx2x_dcbx_stop_hw_tx(struct bnx2x *bp)
+int bnx2x_dcbx_stop_hw_tx(struct bnx2x *bp)
{
struct bnx2x_func_state_params func_params = {NULL};
+ int rc;
func_params.f_obj = &bp->func_obj;
func_params.cmd = BNX2X_F_CMD_TX_STOP;
+ __set_bit(RAMROD_COMP_WAIT, &func_params.ramrod_flags);
+ __set_bit(RAMROD_RETRY, &func_params.ramrod_flags);
+
DP(BNX2X_MSG_DCB, "STOP TRAFFIC\n");
- return bnx2x_func_state_change(bp, &func_params);
+
+ rc = bnx2x_func_state_change(bp, &func_params);
+ if (rc) {
+ BNX2X_ERR("Unable to hold traffic for HW configuration\n");
+ bnx2x_panic();
+ }
+
+ return rc;
}
-static int bnx2x_dcbx_resume_hw_tx(struct bnx2x *bp)
+int bnx2x_dcbx_resume_hw_tx(struct bnx2x *bp)
{
struct bnx2x_func_state_params func_params = {NULL};
struct bnx2x_func_tx_start_params *tx_params =
&func_params.params.tx_start;
+ int rc;
func_params.f_obj = &bp->func_obj;
func_params.cmd = BNX2X_F_CMD_TX_START;
+ __set_bit(RAMROD_COMP_WAIT, &func_params.ramrod_flags);
+ __set_bit(RAMROD_RETRY, &func_params.ramrod_flags);
+
bnx2x_dcbx_fw_struct(bp, tx_params);
DP(BNX2X_MSG_DCB, "START TRAFFIC\n");
- return bnx2x_func_state_change(bp, &func_params);
+
+ rc = bnx2x_func_state_change(bp, &func_params);
+ if (rc) {
+ BNX2X_ERR("Unable to resume traffic after HW configuration\n");
+ bnx2x_panic();
+ }
+
+ return rc;
}
static void bnx2x_dcbx_2cos_limit_update_ets_config(struct bnx2x *bp)
if (IS_MF(bp))
bnx2x_link_sync_notify(bp);
- bnx2x_dcbx_stop_hw_tx(bp);
+ set_bit(BNX2X_SP_RTNL_TX_STOP, &bp->sp_rtnl_state);
+
+ schedule_delayed_work(&bp->sp_rtnl_task, 0);
return;
}
/* ets may affect cmng configuration: reinit it in hw */
bnx2x_set_local_cmng(bp);
- bnx2x_dcbx_resume_hw_tx(bp);
+ set_bit(BNX2X_SP_RTNL_TX_RESUME, &bp->sp_rtnl_state);
+
+ schedule_delayed_work(&bp->sp_rtnl_task, 0);
return;
case BNX2X_DCBX_STATE_TX_RELEASED:
case DCB_FEATCFG_ATTR_PG:
if (bp->dcbx_local_feat.ets.enabled)
*flags |= DCB_FEATCFG_ENABLE;
- if (bp->dcbx_error & DCBX_LOCAL_ETS_ERROR)
+ if (bp->dcbx_error & (DCBX_LOCAL_ETS_ERROR |
+ DCBX_REMOTE_MIB_ERROR))
*flags |= DCB_FEATCFG_ERROR;
break;
case DCB_FEATCFG_ATTR_PFC:
if (bp->dcbx_local_feat.pfc.enabled)
*flags |= DCB_FEATCFG_ENABLE;
if (bp->dcbx_error & (DCBX_LOCAL_PFC_ERROR |
- DCBX_LOCAL_PFC_MISMATCH))
+ DCBX_LOCAL_PFC_MISMATCH |
+ DCBX_REMOTE_MIB_ERROR))
*flags |= DCB_FEATCFG_ERROR;
break;
case DCB_FEATCFG_ATTR_APP:
if (bp->dcbx_local_feat.app.enabled)
*flags |= DCB_FEATCFG_ENABLE;
if (bp->dcbx_error & (DCBX_LOCAL_APP_ERROR |
- DCBX_LOCAL_APP_MISMATCH))
+ DCBX_LOCAL_APP_MISMATCH |
+ DCBX_REMOTE_MIB_ERROR))
*flags |= DCB_FEATCFG_ERROR;
break;
default:
int bnx2x_dcbnl_update_applist(struct bnx2x *bp, bool delall);
#endif /* BCM_DCBNL */
+int bnx2x_dcbx_stop_hw_tx(struct bnx2x *bp);
+int bnx2x_dcbx_resume_hw_tx(struct bnx2x *bp);
+
#endif /* BNX2X_DCB_H */
bp->link_params.req_fc_auto_adv = BNX2X_FLOW_CTRL_BOTH;
}
+static void bnx2x_init_dropless_fc(struct bnx2x *bp)
+{
+ u32 pause_enabled = 0;
+
+ if (!CHIP_IS_E1(bp) && bp->dropless_fc && bp->link_vars.link_up) {
+ if (bp->link_vars.flow_ctrl & BNX2X_FLOW_CTRL_TX)
+ pause_enabled = 1;
+
+ REG_WR(bp, BAR_USTRORM_INTMEM +
+ USTORM_ETH_PAUSE_ENABLED_OFFSET(BP_PORT(bp)),
+ pause_enabled);
+ }
+
+ DP(NETIF_MSG_IFUP | NETIF_MSG_LINK, "dropless_fc is %s\n",
+ pause_enabled ? "enabled" : "disabled");
+}
+
int bnx2x_initial_phy_init(struct bnx2x *bp, int load_mode)
{
int rc, cfx_idx = bnx2x_get_link_cfg_idx(bp);
bnx2x_release_phy_lock(bp);
+ bnx2x_init_dropless_fc(bp);
+
bnx2x_calc_fc_adv(bp);
if (bp->link_vars.link_up) {
bnx2x_phy_init(&bp->link_params, &bp->link_vars);
bnx2x_release_phy_lock(bp);
+ bnx2x_init_dropless_fc(bp);
+
bnx2x_calc_fc_adv(bp);
} else
BNX2X_ERR("Bootcode is missing - can not set link\n");
bnx2x_link_update(&bp->link_params, &bp->link_vars);
- if (bp->link_vars.link_up) {
+ bnx2x_init_dropless_fc(bp);
- /* dropless flow control */
- if (!CHIP_IS_E1(bp) && bp->dropless_fc) {
- int port = BP_PORT(bp);
- u32 pause_enabled = 0;
-
- if (bp->link_vars.flow_ctrl & BNX2X_FLOW_CTRL_TX)
- pause_enabled = 1;
-
- REG_WR(bp, BAR_USTRORM_INTMEM +
- USTORM_ETH_PAUSE_ENABLED_OFFSET(port),
- pause_enabled);
- }
+ if (bp->link_vars.link_up) {
if (bp->link_vars.mac_type != MAC_TYPE_EMAC) {
struct host_port_stats *pstats;
{
int i;
- BNX2X_PCI_FREE(bp->def_status_blk, bp->def_status_blk_mapping,
- sizeof(struct host_sp_status_block));
-
BNX2X_PCI_FREE(bp->fw_stats, bp->fw_stats_mapping,
bp->fw_stats_data_sz + bp->fw_stats_req_sz);
+ if (IS_VF(bp))
+ return;
+
+ BNX2X_PCI_FREE(bp->def_status_blk, bp->def_status_blk_mapping,
+ sizeof(struct host_sp_status_block));
+
BNX2X_PCI_FREE(bp->slowpath, bp->slowpath_mapping,
sizeof(struct bnx2x_slowpath));
&bp->sp_rtnl_state))
bnx2x_pf_set_vfs_vlan(bp);
+ if (test_and_clear_bit(BNX2X_SP_RTNL_TX_STOP, &bp->sp_rtnl_state))
+ bnx2x_dcbx_stop_hw_tx(bp);
+
+ if (test_and_clear_bit(BNX2X_SP_RTNL_TX_RESUME, &bp->sp_rtnl_state))
+ bnx2x_dcbx_resume_hw_tx(bp);
+
/* work which needs rtnl lock not-taken (as it takes the lock itself and
* can be called from other contexts as well)
*/
int tmp;
u32 cfg;
+ if (IS_VF(bp))
+ return 0;
+
if (IS_MF(bp) && !CHIP_IS_E1x(bp)) {
/* Take function: tmp = func */
tmp = BP_ABS_FUNC(bp);
return 0;
}
-static int
-bnx2x_vfop_config_vlan0(struct bnx2x *bp,
- struct bnx2x_vlan_mac_ramrod_params *vlan_mac,
- bool add)
-{
- int rc;
-
- vlan_mac->user_req.cmd = add ? BNX2X_VLAN_MAC_ADD :
- BNX2X_VLAN_MAC_DEL;
- vlan_mac->user_req.u.vlan.vlan = 0;
-
- rc = bnx2x_config_vlan_mac(bp, vlan_mac);
- if (rc == -EEXIST)
- rc = 0;
- return rc;
-}
-
static int bnx2x_vfop_config_list(struct bnx2x *bp,
struct bnx2x_vfop_filters *filters,
struct bnx2x_vlan_mac_ramrod_params *vlan_mac)
case BNX2X_VFOP_VLAN_CONFIG_LIST:
/* next state */
- vfop->state = BNX2X_VFOP_VLAN_CONFIG_LIST_0;
-
- /* remove vlan0 - could be no-op */
- vfop->rc = bnx2x_vfop_config_vlan0(bp, vlan_mac, false);
- if (vfop->rc)
- goto op_err;
+ vfop->state = BNX2X_VFOP_VLAN_MAC_CHK_DONE;
- /* Do vlan list config. if this operation fails we try to
- * restore vlan0 to keep the queue is working order
- */
+ /* do list config */
vfop->rc = bnx2x_vfop_config_list(bp, filters, vlan_mac);
if (!vfop->rc) {
set_bit(RAMROD_CONT, &vlan_mac->ramrod_flags);
vfop->rc = bnx2x_config_vlan_mac(bp, vlan_mac);
}
- bnx2x_vfop_finalize(vf, vfop->rc, VFOP_CONT); /* fall-through */
-
- case BNX2X_VFOP_VLAN_CONFIG_LIST_0:
- /* next state */
- vfop->state = BNX2X_VFOP_VLAN_MAC_CHK_DONE;
-
- if (list_empty(&obj->head))
- /* add vlan0 */
- vfop->rc = bnx2x_vfop_config_vlan0(bp, vlan_mac, true);
bnx2x_vfop_finalize(vf, vfop->rc, VFOP_DONE);
default:
void bnx2x_iov_init_dmae(struct bnx2x *bp)
{
- DP(BNX2X_MSG_IOV, "SRIOV is %s\n", IS_SRIOV(bp) ? "ON" : "OFF");
- if (!IS_SRIOV(bp))
- return;
-
- REG_WR(bp, DMAE_REG_BACKWARD_COMP_EN, 0);
+ if (pci_find_ext_capability(bp->pdev, PCI_EXT_CAP_ID_SRIOV))
+ REG_WR(bp, DMAE_REG_BACKWARD_COMP_EN, 0);
}
static int bnx2x_vf_bus(struct bnx2x *bp, int vfid)
return 0;
}
+struct set_vf_state_cookie {
+ struct bnx2x_virtf *vf;
+ u8 state;
+};
+
+void bnx2x_set_vf_state(void *cookie)
+{
+ struct set_vf_state_cookie *p = (struct set_vf_state_cookie *)cookie;
+
+ p->vf->state = p->state;
+}
+
/* VFOP close (teardown the queues, delete mcasts and close HW) */
static void bnx2x_vfop_close(struct bnx2x *bp, struct bnx2x_virtf *vf)
{
op_err:
BNX2X_ERR("VF[%d] CLOSE error: rc %d\n", vf->abs_vfid, vfop->rc);
op_done:
- vf->state = VF_ACQUIRED;
+
+ /* need to make sure there are no outstanding stats ramrods which may
+ * cause the device to access the VF's stats buffer which it will free
+ * as soon as we return from the close flow.
+ */
+ {
+ struct set_vf_state_cookie cookie;
+
+ cookie.vf = vf;
+ cookie.state = VF_ACQUIRED;
+ bnx2x_stats_safe_exec(bp, bnx2x_set_vf_state, &cookie);
+ }
+
DP(BNX2X_MSG_IOV, "set state to acquired\n");
bnx2x_vfop_end(bp, vf, vfop);
}
pci_disable_sriov(bp->pdev);
}
-static int bnx2x_vf_ndo_sanity(struct bnx2x *bp, int vfidx,
- struct bnx2x_virtf *vf)
+static int bnx2x_vf_ndo_prep(struct bnx2x *bp, int vfidx,
+ struct bnx2x_virtf **vf,
+ struct pf_vf_bulletin_content **bulletin)
{
if (bp->state != BNX2X_STATE_OPEN) {
BNX2X_ERR("vf ndo called though PF is down\n");
return -EINVAL;
}
- if (!vf) {
+ /* init members */
+ *vf = BP_VF(bp, vfidx);
+ *bulletin = BP_VF_BULLETIN(bp, vfidx);
+
+ if (!*vf) {
BNX2X_ERR("vf ndo called but vf was null. vfidx was %d\n",
vfidx);
return -EINVAL;
}
+ if (!*bulletin) {
+ BNX2X_ERR("vf ndo called but Bulletin Board struct is null. vfidx was %d\n",
+ vfidx);
+ return -EINVAL;
+ }
+
return 0;
}
struct ifla_vf_info *ivi)
{
struct bnx2x *bp = netdev_priv(dev);
- struct bnx2x_virtf *vf = BP_VF(bp, vfidx);
- struct bnx2x_vlan_mac_obj *mac_obj = &bnx2x_vfq(vf, 0, mac_obj);
- struct bnx2x_vlan_mac_obj *vlan_obj = &bnx2x_vfq(vf, 0, vlan_obj);
- struct pf_vf_bulletin_content *bulletin = BP_VF_BULLETIN(bp, vfidx);
+ struct bnx2x_virtf *vf = NULL;
+ struct pf_vf_bulletin_content *bulletin = NULL;
+ struct bnx2x_vlan_mac_obj *mac_obj;
+ struct bnx2x_vlan_mac_obj *vlan_obj;
int rc;
- /* sanity */
- rc = bnx2x_vf_ndo_sanity(bp, vfidx, vf);
+ /* sanity and init */
+ rc = bnx2x_vf_ndo_prep(bp, vfidx, &vf, &bulletin);
if (rc)
return rc;
- if (!mac_obj || !vlan_obj || !bulletin) {
+ mac_obj = &bnx2x_vfq(vf, 0, mac_obj);
+ vlan_obj = &bnx2x_vfq(vf, 0, vlan_obj);
+ if (!mac_obj || !vlan_obj) {
BNX2X_ERR("VF partially initialized\n");
return -EINVAL;
}
{
struct bnx2x *bp = netdev_priv(dev);
int rc, q_logical_state;
- struct bnx2x_virtf *vf = BP_VF(bp, vfidx);
- struct pf_vf_bulletin_content *bulletin = BP_VF_BULLETIN(bp, vfidx);
+ struct bnx2x_virtf *vf = NULL;
+ struct pf_vf_bulletin_content *bulletin = NULL;
- /* sanity */
- rc = bnx2x_vf_ndo_sanity(bp, vfidx, vf);
+ /* sanity and init */
+ rc = bnx2x_vf_ndo_prep(bp, vfidx, &vf, &bulletin);
if (rc)
return rc;
if (!is_valid_ether_addr(mac)) {
{
struct bnx2x *bp = netdev_priv(dev);
int rc, q_logical_state;
- struct bnx2x_virtf *vf = BP_VF(bp, vfidx);
- struct pf_vf_bulletin_content *bulletin = BP_VF_BULLETIN(bp, vfidx);
+ struct bnx2x_virtf *vf = NULL;
+ struct pf_vf_bulletin_content *bulletin = NULL;
- /* sanity */
- rc = bnx2x_vf_ndo_sanity(bp, vfidx, vf);
+ /* sanity and init */
+ rc = bnx2x_vf_ndo_prep(bp, vfidx, &vf, &bulletin);
if (rc)
return rc;
/* should be called under stats_sema */
static void __bnx2x_stats_start(struct bnx2x *bp)
{
- /* vfs travel through here as part of the statistics FSM, but no action
- * is required
- */
- if (IS_VF(bp))
- return;
-
- if (bp->port.pmf)
- bnx2x_port_stats_init(bp);
+ if (IS_PF(bp)) {
+ if (bp->port.pmf)
+ bnx2x_port_stats_init(bp);
- else if (bp->func_stx)
- bnx2x_func_stats_init(bp);
+ else if (bp->func_stx)
+ bnx2x_func_stats_init(bp);
- bnx2x_hw_stats_post(bp);
- bnx2x_storm_stats_post(bp);
+ bnx2x_hw_stats_post(bp);
+ bnx2x_storm_stats_post(bp);
+ }
bp->stats_started = true;
}
estats->mac_discard);
}
}
+
+void bnx2x_stats_safe_exec(struct bnx2x *bp,
+ void (func_to_exec)(void *cookie),
+ void *cookie){
+ if (down_timeout(&bp->stats_sema, HZ/10))
+ BNX2X_ERR("Unable to acquire stats lock\n");
+ bnx2x_stats_comp(bp);
+ func_to_exec(cookie);
+ __bnx2x_stats_start(bp);
+ up(&bp->stats_sema);
+}
void bnx2x_memset_stats(struct bnx2x *bp);
void bnx2x_stats_init(struct bnx2x *bp);
void bnx2x_stats_handle(struct bnx2x *bp, enum bnx2x_stats_event event);
+void bnx2x_stats_safe_exec(struct bnx2x *bp,
+ void (func_to_exec)(void *cookie),
+ void *cookie);
/**
* bnx2x_save_statistics - save statistics when unloading.
/* Wait for all pending tx completions to arrive so that
* all tx skbs are freed.
*/
- be_tx_compl_clean(adapter);
netif_tx_disable(netdev);
+ be_tx_compl_clean(adapter);
be_rx_qs_destroy(adapter);
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
+ status = be_fw_wait_ready(adapter);
+ if (status)
+ return status;
+
/* tell fw we're ready to fire cmds */
status = be_cmd_fw_init(adapter);
if (status)
htons(ETH_P_8021Q),
vlan_tag);
- if (!skb_defer_rx_timestamp(skb))
- napi_gro_receive(&fep->napi, skb);
+ napi_gro_receive(&fep->napi, skb);
}
bdp->cbd_bufaddr = dma_map_single(&fep->pdev->dev, data,
jwrite32(jme, JME_APMC, apmc);
}
- NETIF_NAPI_SET(netdev, &jme->napi, jme_poll, jme->rx_ring_size >> 2)
+ NETIF_NAPI_SET(netdev, &jme->napi, jme_poll, NAPI_POLL_WEIGHT)
spin_lock_init(&jme->phy_lock);
spin_lock_init(&jme->macaddr_lock);
#define NETXEN_DB_MAPSIZE_BYTES 0x1000
-#define NETXEN_NETDEV_WEIGHT 128
#define NETXEN_ADAPTER_UP_MAGIC 777
#define NETXEN_NIC_PEG_TUNE 0
for (ring = 0; ring < adapter->max_sds_rings; ring++) {
sds_ring = &recv_ctx->sds_rings[ring];
netif_napi_add(netdev, &sds_ring->napi,
- netxen_nic_poll, NETXEN_NETDEV_WEIGHT);
+ netxen_nic_poll, NAPI_POLL_WEIGHT);
}
return 0;
RTL_W8(Cfg9346, Cfg9346_Unlock);
RTL_W8(Config1, RTL_R8(Config1) | PMEnable);
- RTL_W8(Config5, RTL_R8(Config5) & PMEStatus);
+ RTL_W8(Config5, RTL_R8(Config5) & (BWF | MWF | UWF | LanWake | PMEStatus));
if ((RTL_R8(Config3) & (LinkUp | MagicPacket)) != 0)
tp->features |= RTL_FEATURE_WOL;
if ((RTL_R8(Config5) & (UWF | BWF | MWF)) != 0)
BUILD_BUG_ON(EFX_FILTER_INDEX_UC_DEF != 0);
BUILD_BUG_ON(EFX_FILTER_INDEX_MC_DEF !=
EFX_FILTER_MC_DEF - EFX_FILTER_UC_DEF);
- rep_index = spec->type - EFX_FILTER_INDEX_UC_DEF;
+ rep_index = spec->type - EFX_FILTER_UC_DEF;
ins_index = rep_index;
spin_lock_bh(&state->lock);
plat->force_sf_dma_mode = 1;
}
- dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*dma_cfg), GFP_KERNEL);
- if (!dma_cfg)
- return -ENOMEM;
-
- plat->dma_cfg = dma_cfg;
- of_property_read_u32(np, "snps,pbl", &dma_cfg->pbl);
- dma_cfg->fixed_burst = of_property_read_bool(np, "snps,fixed-burst");
- dma_cfg->mixed_burst = of_property_read_bool(np, "snps,mixed-burst");
+ if (of_find_property(np, "snps,pbl", NULL)) {
+ dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*dma_cfg),
+ GFP_KERNEL);
+ if (!dma_cfg)
+ return -ENOMEM;
+ plat->dma_cfg = dma_cfg;
+ of_property_read_u32(np, "snps,pbl", &dma_cfg->pbl);
+ dma_cfg->fixed_burst =
+ of_property_read_bool(np, "snps,fixed-burst");
+ dma_cfg->mixed_burst =
+ of_property_read_bool(np, "snps,mixed-burst");
+ }
return 0;
}
{
netdev->watchdog_timeo = GELIC_NET_WATCHDOG_TIMEOUT;
/* NAPI */
- netif_napi_add(netdev, napi,
- gelic_net_poll, GELIC_NET_NAPI_WEIGHT);
+ netif_napi_add(netdev, napi, gelic_net_poll, NAPI_POLL_WEIGHT);
netdev->ethtool_ops = &gelic_ether_ethtool_ops;
netdev->netdev_ops = &gelic_netdevice_ops;
}
#define GELIC_NET_RXBUF_ALIGN 128
#define GELIC_CARD_RX_CSUM_DEFAULT 1 /* hw chksum */
#define GELIC_NET_WATCHDOG_TIMEOUT 5*HZ
-#define GELIC_NET_NAPI_WEIGHT (GELIC_NET_RX_DESCRIPTORS)
#define GELIC_NET_BROADCAST_ADDR 0xffffffffffffL
#define GELIC_NET_MC_COUNT_MAX 32 /* multicast address list */
printk(KERN_WARNING "Setting MDIO clock divisor to "
"default %d\n", DEFAULT_CLOCK_DIVISOR);
clk_div = DEFAULT_CLOCK_DIVISOR;
+ of_node_put(np1);
goto issue;
}
pci_write_config_byte(pcidev,0x42,(bTmp | 0xf0));
pci_write_config_byte(pcidev,0x5a,0xc0);
WriteLPCReg(0x28, 0x70 );
- if (via_ircc_open(pcidev, &info, 0x3076) == 0)
- rc=0;
+ rc = via_ircc_open(pcidev, &info, 0x3076);
} else
rc = -ENODEV; //IR not turn on
} else { //Not VT1211
info.irq=FirIRQ;
info.dma=FirDRQ1;
info.dma2=FirDRQ0;
- if (via_ircc_open(pcidev, &info, 0x3096) == 0)
- rc=0;
+ rc = via_ircc_open(pcidev, &info, 0x3096);
} else
rc = -ENODEV; //IR not turn on !!!!!
}//Not VT1211
#define TUN_OFFLOADS (NETIF_F_HW_CSUM | NETIF_F_TSO_ECN | NETIF_F_TSO | \
NETIF_F_TSO6 | NETIF_F_UFO)
#define RX_OFFLOADS (NETIF_F_GRO | NETIF_F_LRO)
+#define TAP_FEATURES (NETIF_F_GSO | NETIF_F_SG)
+
/*
* RCU usage:
* The macvtap_queue and the macvlan_dev are loosely coupled, the
{
struct macvlan_dev *vlan = netdev_priv(dev);
struct macvtap_queue *q = macvtap_get_queue(dev, skb);
- netdev_features_t features;
+ netdev_features_t features = TAP_FEATURES;
+
if (!q)
goto drop;
skb->dev = dev;
/* Apply the forward feature mask so that we perform segmentation
- * according to users wishes.
+ * according to users wishes. This only works if VNET_HDR is
+ * enabled.
*/
- features = netif_skb_features(skb) & vlan->tap_features;
+ if (q->flags & IFF_VNET_HDR)
+ features |= vlan->tap_features;
if (netif_needs_gso(skb, features)) {
struct sk_buff *segs = __skb_gso_segment(skb, features, false);
/* tap_features are the same as features on tun/tap and
* reflect user expectations.
*/
- vlan->tap_features = vlan->dev->features &
- (feature_mask | ~TUN_OFFLOADS);
+ vlan->tap_features = feature_mask;
vlan->set_features = features;
netdev_update_features(vlan->dev);
TUN_F_TSO_ECN | TUN_F_UFO))
return -EINVAL;
- /* TODO: only accept frames with the features that
- got enabled for forwarded frames */
- if (!(q->flags & IFF_VNET_HDR))
- return -EINVAL;
rtnl_lock();
ret = set_offload(q, arg);
rtnl_unlock();
#define RTL821x_INER_INIT 0x6400
#define RTL821x_INSR 0x13
-#define RTL8211E_INER_LINK_STAT 0x10
+#define RTL8211E_INER_LINK_STATUS 0x400
MODULE_DESCRIPTION("Realtek PHY driver");
MODULE_AUTHOR("Johnson Leung");
if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
err = phy_write(phydev, RTL821x_INER,
- RTL8211E_INER_LINK_STAT);
+ RTL8211E_INER_LINK_STATUS);
else
err = phy_write(phydev, RTL821x_INER, 0);
{ USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x68a2, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE),
.driver_info = (unsigned long)&cdc_mbim_info_zlp,
},
+ /* HP hs2434 Mobile Broadband Module needs ZLPs */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x3f0, 0x4b1d, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE),
+ .driver_info = (unsigned long)&cdc_mbim_info_zlp,
+ },
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE),
.driver_info = (unsigned long)&cdc_mbim_info,
},
static int hso_get_config_data(struct usb_interface *interface)
{
struct usb_device *usbdev = interface_to_usbdev(interface);
- u8 config_data[17];
+ u8 *config_data = kmalloc(17, GFP_KERNEL);
u32 if_num = interface->altsetting->desc.bInterfaceNumber;
s32 result;
+ if (!config_data)
+ return -ENOMEM;
if (usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),
0x86, 0xC0, 0, 0, config_data, 17,
USB_CTRL_SET_TIMEOUT) != 0x11) {
+ kfree(config_data);
return -EIO;
}
if (config_data[16] & 0x1)
result |= HSO_INFO_CRC_BUG;
+ kfree(config_data);
return result;
}
struct hso_shared_int *shared_int;
struct hso_device *tmp_dev = NULL;
+ if (interface->cur_altsetting->desc.bInterfaceClass != 0xFF) {
+ dev_err(&interface->dev, "Not our interface\n");
+ return -ENODEV;
+ }
+
if_num = interface->altsetting->desc.bInterfaceNumber;
/* Get the interface/port specification from either driver_info or from
else
port_spec = hso_get_config_data(interface);
- if (interface->cur_altsetting->desc.bInterfaceClass != 0xFF) {
- dev_err(&interface->dev, "Not our interface\n");
- return -ENODEV;
- }
/* Check if we need to switch to alt interfaces prior to port
* configuration */
if (interface->num_altsetting > 1)
struct ieee80211_conf *cur_conf = &priv->hw->conf;
bool txok;
int slot;
+ int hdrlen, padsize;
slot = strip_drv_header(priv, skb);
if (slot < 0) {
ath9k_htc_tx_clear_slot(priv, slot);
+ /* Remove padding before handing frame back to mac80211 */
+ hdrlen = ieee80211_get_hdrlen_from_skb(skb);
+
+ padsize = hdrlen & 3;
+ if (padsize && skb->len > hdrlen + padsize) {
+ memmove(skb->data + padsize, skb->data, hdrlen);
+ skb_pull(skb, padsize);
+ }
+
/* Send status to mac80211 */
ieee80211_tx_status(priv->hw, skb);
}
IEEE80211_HW_PS_NULLFUNC_STACK |
IEEE80211_HW_SPECTRUM_MGMT |
IEEE80211_HW_REPORTS_TX_ACK_STATUS |
- IEEE80211_HW_SUPPORTS_RC_TABLE;
+ IEEE80211_HW_SUPPORTS_RC_TABLE |
+ IEEE80211_HW_SUPPORTS_HT_CCK_RATES;
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) {
hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;
{
ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
- if (AR_SREV_9340(sc->sc_ah) || AR_SREV_9485(sc->sc_ah) ||
- AR_SREV_9550(sc->sc_ah))
+ if (AR_SREV_9340(sc->sc_ah) || AR_SREV_9330(sc->sc_ah))
ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work,
msecs_to_jiffies(ATH_PLL_WORK_INTERVAL));
IEEE80211_HW_PS_NULLFUNC_STACK |
IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC |
IEEE80211_HW_SUPPORTS_RC_TABLE |
- IEEE80211_HW_SIGNAL_DBM;
+ IEEE80211_HW_SIGNAL_DBM |
+ IEEE80211_HW_SUPPORTS_HT_CCK_RATES;
if (!modparam_noht) {
/*
data->length = prism2_ap_get_sta_qual(local, addr, qual, IW_MAX_AP, 1);
- memcpy(extra, &addr, sizeof(struct sockaddr) * data->length);
+ memcpy(extra, addr, sizeof(struct sockaddr) * data->length);
data->flags = 1; /* has quality information */
- memcpy(extra + sizeof(struct sockaddr) * data->length, &qual,
+ memcpy(extra + sizeof(struct sockaddr) * data->length, qual,
sizeof(struct iw_quality) * data->length);
kfree(addr);
set_bit(S_RFKILL, &il->status);
} else {
clear_bit(S_RFKILL, &il->status);
- wiphy_rfkill_set_hw_state(il->hw->wiphy, hw_rf_kill);
il_force_reset(il, true);
}
+ wiphy_rfkill_set_hw_state(il->hw->wiphy, hw_rf_kill);
handled |= CSR_INT_BIT_RF_KILL;
}
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
return;
- if (test_and_clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status))
+ if (!test_and_clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status))
+ return;
+
+ if (ctx->vif)
ieee80211_chswitch_done(ctx->vif, is_success);
}
#define APMG_PCIDEV_STT_VAL_L1_ACT_DIS (0x00000800)
-#define APMG_RTC_INT_STT_RFKILL (0x10000000)
-
/* Device system time */
#define DEVICE_SYSTEM_TIME_REG 0xA0206C
schedule_work(&mvm->roc_done_wk);
}
+static bool iwl_mvm_te_check_disconnect(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ const char *errmsg)
+{
+ if (vif->type != NL80211_IFTYPE_STATION)
+ return false;
+ if (vif->bss_conf.assoc && vif->bss_conf.dtim_period)
+ return false;
+ if (errmsg)
+ IWL_ERR(mvm, "%s\n", errmsg);
+ ieee80211_connection_loss(vif);
+ return true;
+}
+
/*
* Handles a FW notification for an event that is known to the driver.
*
* P2P Device discoveribility, while there are other higher priority
* events in the system).
*/
- WARN_ONCE(!le32_to_cpu(notif->status),
- "Failed to schedule time event\n");
+ if (WARN_ONCE(!le32_to_cpu(notif->status),
+ "Failed to schedule time event\n")) {
+ if (iwl_mvm_te_check_disconnect(mvm, te_data->vif, NULL)) {
+ iwl_mvm_te_clear_data(mvm, te_data);
+ return;
+ }
+ }
if (le32_to_cpu(notif->action) & TE_NOTIF_HOST_EVENT_END) {
IWL_DEBUG_TE(mvm,
* By now, we should have finished association
* and know the dtim period.
*/
- if (te_data->vif->type == NL80211_IFTYPE_STATION &&
- (!te_data->vif->bss_conf.assoc ||
- !te_data->vif->bss_conf.dtim_period)) {
- IWL_ERR(mvm,
- "No assocation and the time event is over already...\n");
- ieee80211_connection_loss(te_data->vif);
- }
-
+ iwl_mvm_te_check_disconnect(mvm, te_data->vif,
+ "No assocation and the time event is over already...");
iwl_mvm_te_clear_data(mvm, te_data);
} else if (le32_to_cpu(notif->action) & TE_NOTIF_HOST_EVENT_START) {
te_data->running = true;
iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill);
if (hw_rfkill) {
- /*
- * Clear the interrupt in APMG if the NIC is going down.
- * Note that when the NIC exits RFkill (else branch), we
- * can't access prph and the NIC will be reset in
- * start_hw anyway.
- */
- iwl_write_prph(trans, APMG_RTC_INT_STT_REG,
- APMG_RTC_INT_STT_RFKILL);
set_bit(STATUS_RFKILL, &trans_pcie->status);
if (test_and_clear_bit(STATUS_HCMD_ACTIVE,
&trans_pcie->status))
spin_lock_init(&trans_pcie->reg_lock);
init_waitqueue_head(&trans_pcie->ucode_write_waitq);
- /* W/A - seems to solve weird behavior. We need to remove this if we
- * don't want to stay in L1 all the time. This wastes a lot of power */
- pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 |
- PCIE_LINK_STATE_CLKPM);
-
if (pci_enable_device(pdev)) {
err = -ENODEV;
goto out_no_pci;
}
+ /* W/A - seems to solve weird behavior. We need to remove this if we
+ * don't want to stay in L1 all the time. This wastes a lot of power */
+ pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 |
+ PCIE_LINK_STATE_CLKPM);
+
pci_set_master(pdev);
err = pci_set_dma_mask(pdev, DMA_BIT_MASK(36));
IEEE80211_HW_SUPPORTS_PS |
IEEE80211_HW_PS_NULLFUNC_STACK |
IEEE80211_HW_AMPDU_AGGREGATION |
- IEEE80211_HW_REPORTS_TX_ACK_STATUS;
+ IEEE80211_HW_REPORTS_TX_ACK_STATUS |
+ IEEE80211_HW_SUPPORTS_HT_CCK_RATES;
/*
* Don't set IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING for USB devices
goto exit;
err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 0x4,
- USB_DIR_IN | 0x40, 0,0, &ret, sizeof(ret), ZD1201_FW_TIMEOUT);
+ USB_DIR_IN | 0x40, 0, 0, buf, sizeof(ret), ZD1201_FW_TIMEOUT);
if (err < 0)
goto exit;
+ memcpy(&ret, buf, sizeof(ret));
+
if (ret & 0x80) {
err = -EIO;
goto exit;
mem = (unsigned long)
dt_alloc(size + 4, __alignof__(struct device_node));
+ memset((void *)mem, 0, size);
+
((__be32 *)mem)[size / 4] = cpu_to_be32(0xdeadbeef);
pr_debug(" unflattening %lx...\n", mem);
When in doubt, say N.
config HOTPLUG_PCI_S390
- tristate "System z PCI Hotplug Support"
+ bool "System z PCI Hotplug Support"
depends on S390 && 64BIT
help
Say Y here if you want to use the System z PCI Hotplug
if (rc)
goto out_deconfigure;
- slot->zdev->state = ZPCI_FN_STATE_ONLINE;
-
pci_scan_slot(slot->zdev->bus, ZPCI_DEVFN);
pci_bus_add_devices(slot->zdev->bus);
.get_adapter_status = get_adapter_status,
};
-static int init_pci_slot(struct zpci_dev *zdev)
+int zpci_init_slot(struct zpci_dev *zdev)
{
struct hotplug_slot *hotplug_slot;
struct hotplug_slot_info *info;
return -ENOMEM;
}
-static void exit_pci_slot(struct zpci_dev *zdev)
+void zpci_exit_slot(struct zpci_dev *zdev)
{
struct list_head *tmp, *n;
struct slot *slot;
pci_hp_deregister(slot->hotplug_slot);
}
}
-
-static struct pci_hp_callback_ops hp_ops = {
- .create_slot = init_pci_slot,
- .remove_slot = exit_pci_slot,
-};
-
-static void __init init_pci_slots(void)
-{
- struct zpci_dev *zdev;
-
- /*
- * Create a structure for each slot, and register that slot
- * with the pci_hotplug subsystem.
- */
- mutex_lock(&zpci_list_lock);
- list_for_each_entry(zdev, &zpci_list, entry) {
- init_pci_slot(zdev);
- }
- mutex_unlock(&zpci_list_lock);
-}
-
-static void __exit exit_pci_slots(void)
-{
- struct list_head *tmp, *n;
- struct slot *slot;
-
- /*
- * Unregister all of our slots with the pci_hotplug subsystem.
- * Memory will be freed in release_slot() callback after slot's
- * lifespan is finished.
- */
- list_for_each_safe(tmp, n, &s390_hotplug_slot_list) {
- slot = list_entry(tmp, struct slot, slot_list);
- list_del(&slot->slot_list);
- pci_hp_deregister(slot->hotplug_slot);
- }
-}
-
-static int __init pci_hotplug_s390_init(void)
-{
- if (!s390_pci_probe)
- return -EOPNOTSUPP;
-
- zpci_register_hp_ops(&hp_ops);
- init_pci_slots();
-
- return 0;
-}
-
-static void __exit pci_hotplug_s390_exit(void)
-{
- exit_pci_slots();
- zpci_deregister_hp_ops();
-}
-
-module_init(pci_hotplug_s390_init);
-module_exit(pci_hotplug_s390_exit);
{
struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
struct sunxi_pinctrl_group *g = &pctl->groups[group];
+ unsigned long flags;
u32 val, mask;
u16 strength;
u8 dlevel;
* 3: 40mA
*/
dlevel = strength / 10 - 1;
+
+ spin_lock_irqsave(&pctl->lock, flags);
+
val = readl(pctl->membase + sunxi_dlevel_reg(g->pin));
mask = DLEVEL_PINS_MASK << sunxi_dlevel_offset(g->pin);
writel((val & ~mask) | dlevel << sunxi_dlevel_offset(g->pin),
pctl->membase + sunxi_dlevel_reg(g->pin));
+
+ spin_unlock_irqrestore(&pctl->lock, flags);
break;
case PIN_CONFIG_BIAS_PULL_UP:
+ spin_lock_irqsave(&pctl->lock, flags);
+
val = readl(pctl->membase + sunxi_pull_reg(g->pin));
mask = PULL_PINS_MASK << sunxi_pull_offset(g->pin);
writel((val & ~mask) | 1 << sunxi_pull_offset(g->pin),
pctl->membase + sunxi_pull_reg(g->pin));
+
+ spin_unlock_irqrestore(&pctl->lock, flags);
break;
case PIN_CONFIG_BIAS_PULL_DOWN:
+ spin_lock_irqsave(&pctl->lock, flags);
+
val = readl(pctl->membase + sunxi_pull_reg(g->pin));
mask = PULL_PINS_MASK << sunxi_pull_offset(g->pin);
writel((val & ~mask) | 2 << sunxi_pull_offset(g->pin),
pctl->membase + sunxi_pull_reg(g->pin));
+
+ spin_unlock_irqrestore(&pctl->lock, flags);
break;
default:
break;
u8 config)
{
struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+ unsigned long flags;
+ u32 val, mask;
+
+ spin_lock_irqsave(&pctl->lock, flags);
- u32 val = readl(pctl->membase + sunxi_mux_reg(pin));
- u32 mask = MUX_PINS_MASK << sunxi_mux_offset(pin);
+ val = readl(pctl->membase + sunxi_mux_reg(pin));
+ mask = MUX_PINS_MASK << sunxi_mux_offset(pin);
writel((val & ~mask) | config << sunxi_mux_offset(pin),
pctl->membase + sunxi_mux_reg(pin));
+
+ spin_unlock_irqrestore(&pctl->lock, flags);
}
static int sunxi_pmx_enable(struct pinctrl_dev *pctldev,
struct sunxi_pinctrl *pctl = dev_get_drvdata(chip->dev);
u32 reg = sunxi_data_reg(offset);
u8 index = sunxi_data_offset(offset);
+ unsigned long flags;
+ u32 regval;
+
+ spin_lock_irqsave(&pctl->lock, flags);
+
+ regval = readl(pctl->membase + reg);
- writel((value & DATA_PINS_MASK) << index, pctl->membase + reg);
+ if (value)
+ regval |= BIT(index);
+ else
+ regval &= ~(BIT(index));
+
+ writel(regval, pctl->membase + reg);
+
+ spin_unlock_irqrestore(&pctl->lock, flags);
}
static int sunxi_pinctrl_gpio_of_xlate(struct gpio_chip *gc,
struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d);
u32 reg = sunxi_irq_cfg_reg(d->hwirq);
u8 index = sunxi_irq_cfg_offset(d->hwirq);
+ unsigned long flags;
+ u32 regval;
u8 mode;
switch (type) {
return -EINVAL;
}
- writel((mode & IRQ_CFG_IRQ_MASK) << index, pctl->membase + reg);
+ spin_lock_irqsave(&pctl->lock, flags);
+
+ regval = readl(pctl->membase + reg);
+ regval &= ~IRQ_CFG_IRQ_MASK;
+ writel(regval | (mode << index), pctl->membase + reg);
+
+ spin_unlock_irqrestore(&pctl->lock, flags);
return 0;
}
u8 ctrl_idx = sunxi_irq_ctrl_offset(d->hwirq);
u32 status_reg = sunxi_irq_status_reg(d->hwirq);
u8 status_idx = sunxi_irq_status_offset(d->hwirq);
+ unsigned long flags;
u32 val;
+ spin_lock_irqsave(&pctl->lock, flags);
+
/* Mask the IRQ */
val = readl(pctl->membase + ctrl_reg);
writel(val & ~(1 << ctrl_idx), pctl->membase + ctrl_reg);
/* Clear the IRQ */
writel(1 << status_idx, pctl->membase + status_reg);
+
+ spin_unlock_irqrestore(&pctl->lock, flags);
}
static void sunxi_pinctrl_irq_mask(struct irq_data *d)
struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d);
u32 reg = sunxi_irq_ctrl_reg(d->hwirq);
u8 idx = sunxi_irq_ctrl_offset(d->hwirq);
+ unsigned long flags;
u32 val;
+ spin_lock_irqsave(&pctl->lock, flags);
+
/* Mask the IRQ */
val = readl(pctl->membase + reg);
writel(val & ~(1 << idx), pctl->membase + reg);
+
+ spin_unlock_irqrestore(&pctl->lock, flags);
}
static void sunxi_pinctrl_irq_unmask(struct irq_data *d)
struct sunxi_desc_function *func;
u32 reg = sunxi_irq_ctrl_reg(d->hwirq);
u8 idx = sunxi_irq_ctrl_offset(d->hwirq);
+ unsigned long flags;
u32 val;
func = sunxi_pinctrl_desc_find_function_by_pin(pctl,
/* Change muxing to INT mode */
sunxi_pmx_set(pctl->pctl_dev, pctl->irq_array[d->hwirq], func->muxval);
+ spin_lock_irqsave(&pctl->lock, flags);
+
/* Unmask the IRQ */
val = readl(pctl->membase + reg);
writel(val | (1 << idx), pctl->membase + reg);
+
+ spin_unlock_irqrestore(&pctl->lock, flags);
}
static struct irq_chip sunxi_pinctrl_irq_chip = {
return -ENOMEM;
platform_set_drvdata(pdev, pctl);
+ spin_lock_init(&pctl->lock);
+
pctl->membase = of_iomap(node, 0);
if (!pctl->membase)
return -ENOMEM;
#define __PINCTRL_SUNXI_H
#include <linux/kernel.h>
+#include <linux/spinlock.h>
#define PA_BASE 0
#define PB_BASE 32
unsigned ngroups;
int irq;
int irq_array[SUNXI_IRQ_NUMBER];
+ spinlock_t lock;
struct pinctrl_dev *pctl_dev;
};
return platform_driver_register(&olpc_ec_plat_driver);
}
-module_init(olpc_ec_init_module);
+arch_initcall(olpc_ec_init_module);
MODULE_AUTHOR("Andres Salomon <dilinger@queued.net>");
MODULE_LICENSE("GPL");
#define HPWMI_ALS_QUERY 0x3
#define HPWMI_HARDWARE_QUERY 0x4
#define HPWMI_WIRELESS_QUERY 0x5
-#define HPWMI_BIOS_QUERY 0x9
#define HPWMI_HOTKEY_QUERY 0xc
#define HPWMI_WIRELESS2_QUERY 0x1b
#define HPWMI_POSTCODEERROR_QUERY 0x2a
return (state & 0x4) ? 1 : 0;
}
-static int hp_wmi_enable_hotkeys(void)
-{
- int ret;
- int query = 0x6e;
-
- ret = hp_wmi_perform_query(HPWMI_BIOS_QUERY, 1, &query, sizeof(query),
- 0);
-
- if (ret)
- return -EINVAL;
- return 0;
-}
-
static int hp_wmi_set_block(void *data, bool blocked)
{
enum hp_wmi_radio r = (enum hp_wmi_radio) data;
err = hp_wmi_input_setup();
if (err)
return err;
-
- hp_wmi_enable_hotkeys();
}
if (bios_capable) {
if (pos < 0)
return pos;
- return snprintf(buffer, PAGE_SIZE, "%s\n", pos ? "speed" : "stamina");
+ return snprintf(buffer, PAGE_SIZE, "%s\n",
+ pos == SPEED ? "speed" :
+ pos == STAMINA ? "stamina" :
+ pos == AUTO ? "auto" : "unknown");
}
static int sony_nc_gfx_switch_setup(struct platform_device *pd,
goto err_free_resources;
}
- if (sonypi_compat_init())
+ result = sonypi_compat_init();
+ if (result)
goto err_remove_input;
/* request io port */
--- /dev/null
+/*
+ * Regulators driver for Marvell 88PM800
+ *
+ * Copyright (C) 2012 Marvell International Ltd.
+ * Joseph(Yossi) Hanin <yhanin@marvell.com>
+ * Yi Zhang <yizhang@marvell.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.
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/88pm80x.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/regulator/of_regulator.h>
+
+/* LDO1 with DVC[0..3] */
+#define PM800_LDO1_VOUT (0x08) /* VOUT1 */
+#define PM800_LDO1_VOUT_2 (0x09)
+#define PM800_LDO1_VOUT_3 (0x0A)
+#define PM800_LDO2_VOUT (0x0B)
+#define PM800_LDO3_VOUT (0x0C)
+#define PM800_LDO4_VOUT (0x0D)
+#define PM800_LDO5_VOUT (0x0E)
+#define PM800_LDO6_VOUT (0x0F)
+#define PM800_LDO7_VOUT (0x10)
+#define PM800_LDO8_VOUT (0x11)
+#define PM800_LDO9_VOUT (0x12)
+#define PM800_LDO10_VOUT (0x13)
+#define PM800_LDO11_VOUT (0x14)
+#define PM800_LDO12_VOUT (0x15)
+#define PM800_LDO13_VOUT (0x16)
+#define PM800_LDO14_VOUT (0x17)
+#define PM800_LDO15_VOUT (0x18)
+#define PM800_LDO16_VOUT (0x19)
+#define PM800_LDO17_VOUT (0x1A)
+#define PM800_LDO18_VOUT (0x1B)
+#define PM800_LDO19_VOUT (0x1C)
+
+/* BUCK1 with DVC[0..3] */
+#define PM800_BUCK1 (0x3C)
+#define PM800_BUCK1_1 (0x3D)
+#define PM800_BUCK1_2 (0x3E)
+#define PM800_BUCK1_3 (0x3F)
+#define PM800_BUCK2 (0x40)
+#define PM800_BUCK3 (0x41)
+#define PM800_BUCK3 (0x41)
+#define PM800_BUCK4 (0x42)
+#define PM800_BUCK4_1 (0x43)
+#define PM800_BUCK4_2 (0x44)
+#define PM800_BUCK4_3 (0x45)
+#define PM800_BUCK5 (0x46)
+
+#define PM800_BUCK_ENA (0x50)
+#define PM800_LDO_ENA1_1 (0x51)
+#define PM800_LDO_ENA1_2 (0x52)
+#define PM800_LDO_ENA1_3 (0x53)
+
+#define PM800_LDO_ENA2_1 (0x56)
+#define PM800_LDO_ENA2_2 (0x57)
+#define PM800_LDO_ENA2_3 (0x58)
+
+#define PM800_BUCK1_MISC1 (0x78)
+#define PM800_BUCK3_MISC1 (0x7E)
+#define PM800_BUCK4_MISC1 (0x81)
+#define PM800_BUCK5_MISC1 (0x84)
+
+struct pm800_regulator_info {
+ struct regulator_desc desc;
+ int max_ua;
+};
+
+struct pm800_regulators {
+ struct regulator_dev *regulators[PM800_ID_RG_MAX];
+ struct pm80x_chip *chip;
+ struct regmap *map;
+};
+
+/*
+ * vreg - the buck regs string.
+ * ereg - the string for the enable register.
+ * ebit - the bit number in the enable register.
+ * amax - the current
+ * Buck has 2 kinds of voltage steps. It is easy to find voltage by ranges,
+ * not the constant voltage table.
+ * n_volt - Number of available selectors
+ */
+#define PM800_BUCK(vreg, ereg, ebit, amax, volt_ranges, n_volt) \
+{ \
+ .desc = { \
+ .name = #vreg, \
+ .ops = &pm800_volt_range_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = PM800_ID_##vreg, \
+ .owner = THIS_MODULE, \
+ .n_voltages = n_volt, \
+ .linear_ranges = volt_ranges, \
+ .n_linear_ranges = ARRAY_SIZE(volt_ranges), \
+ .vsel_reg = PM800_##vreg, \
+ .vsel_mask = 0x7f, \
+ .enable_reg = PM800_##ereg, \
+ .enable_mask = 1 << (ebit), \
+ }, \
+ .max_ua = (amax), \
+}
+
+/*
+ * vreg - the LDO regs string
+ * ereg - the string for the enable register.
+ * ebit - the bit number in the enable register.
+ * amax - the current
+ * volt_table - the LDO voltage table
+ * For all the LDOes, there are too many ranges. Using volt_table will be
+ * simpler and faster.
+ */
+#define PM800_LDO(vreg, ereg, ebit, amax, ldo_volt_table) \
+{ \
+ .desc = { \
+ .name = #vreg, \
+ .ops = &pm800_volt_table_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = PM800_ID_##vreg, \
+ .owner = THIS_MODULE, \
+ .n_voltages = ARRAY_SIZE(ldo_volt_table), \
+ .vsel_reg = PM800_##vreg##_VOUT, \
+ .vsel_mask = 0x1f, \
+ .enable_reg = PM800_##ereg, \
+ .enable_mask = 1 << (ebit), \
+ .volt_table = ldo_volt_table, \
+ }, \
+ .max_ua = (amax), \
+}
+
+/* Ranges are sorted in ascending order. */
+static const struct regulator_linear_range buck1_volt_range[] = {
+ { .min_uV = 600000, .max_uV = 1587500, .min_sel = 0, .max_sel = 0x4f,
+ .uV_step = 12500 },
+ { .min_uV = 1600000, .max_uV = 1800000, .min_sel = 0x50,
+ .max_sel = 0x54, .uV_step = 50000 },
+};
+
+/* BUCK 2~5 have same ranges. */
+static const struct regulator_linear_range buck2_5_volt_range[] = {
+ { .min_uV = 600000, .max_uV = 1587500, .min_sel = 0, .max_sel = 0x4f,
+ .uV_step = 12500 },
+ { .min_uV = 1600000, .max_uV = 3300000, .min_sel = 0x50,
+ .max_sel = 0x72, .uV_step = 50000 },
+};
+
+static const unsigned int ldo1_volt_table[] = {
+ 600000, 650000, 700000, 750000, 800000, 850000, 900000, 950000,
+ 1000000, 1050000, 1100000, 1150000, 1200000, 1300000, 1400000, 1500000,
+};
+
+static const unsigned int ldo2_volt_table[] = {
+ 1700000, 1800000, 1900000, 2000000, 2100000, 2500000, 2700000, 2800000,
+};
+
+/* LDO 3~17 have same voltage table. */
+static const unsigned int ldo3_17_volt_table[] = {
+ 1200000, 1250000, 1700000, 1800000, 1850000, 1900000, 2500000, 2600000,
+ 2700000, 2750000, 2800000, 2850000, 2900000, 3000000, 3100000, 3300000,
+};
+
+/* LDO 18~19 have same voltage table. */
+static const unsigned int ldo18_19_volt_table[] = {
+ 1700000, 1800000, 1900000, 2500000, 2800000, 2900000, 3100000, 3300000,
+};
+
+static int pm800_get_current_limit(struct regulator_dev *rdev)
+{
+ struct pm800_regulator_info *info = rdev_get_drvdata(rdev);
+
+ return info->max_ua;
+}
+
+static struct regulator_ops pm800_volt_range_ops = {
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .get_current_limit = pm800_get_current_limit,
+};
+
+static struct regulator_ops pm800_volt_table_ops = {
+ .list_voltage = regulator_list_voltage_table,
+ .map_voltage = regulator_map_voltage_iterate,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .get_current_limit = pm800_get_current_limit,
+};
+
+/* The array is indexed by id(PM800_ID_XXX) */
+static struct pm800_regulator_info pm800_regulator_info[] = {
+ PM800_BUCK(BUCK1, BUCK_ENA, 0, 3000000, buck1_volt_range, 0x55),
+ PM800_BUCK(BUCK2, BUCK_ENA, 1, 1200000, buck2_5_volt_range, 0x73),
+ PM800_BUCK(BUCK3, BUCK_ENA, 2, 1200000, buck2_5_volt_range, 0x73),
+ PM800_BUCK(BUCK4, BUCK_ENA, 3, 1200000, buck2_5_volt_range, 0x73),
+ PM800_BUCK(BUCK5, BUCK_ENA, 4, 1200000, buck2_5_volt_range, 0x73),
+
+ PM800_LDO(LDO1, LDO_ENA1_1, 0, 200000, ldo1_volt_table),
+ PM800_LDO(LDO2, LDO_ENA1_1, 1, 10000, ldo2_volt_table),
+ PM800_LDO(LDO3, LDO_ENA1_1, 2, 300000, ldo3_17_volt_table),
+ PM800_LDO(LDO4, LDO_ENA1_1, 3, 300000, ldo3_17_volt_table),
+ PM800_LDO(LDO5, LDO_ENA1_1, 4, 300000, ldo3_17_volt_table),
+ PM800_LDO(LDO6, LDO_ENA1_1, 5, 300000, ldo3_17_volt_table),
+ PM800_LDO(LDO7, LDO_ENA1_1, 6, 300000, ldo3_17_volt_table),
+ PM800_LDO(LDO8, LDO_ENA1_1, 7, 300000, ldo3_17_volt_table),
+ PM800_LDO(LDO9, LDO_ENA1_2, 0, 300000, ldo3_17_volt_table),
+ PM800_LDO(LDO10, LDO_ENA1_2, 1, 300000, ldo3_17_volt_table),
+ PM800_LDO(LDO11, LDO_ENA1_2, 2, 300000, ldo3_17_volt_table),
+ PM800_LDO(LDO12, LDO_ENA1_2, 3, 300000, ldo3_17_volt_table),
+ PM800_LDO(LDO13, LDO_ENA1_2, 4, 300000, ldo3_17_volt_table),
+ PM800_LDO(LDO14, LDO_ENA1_2, 5, 300000, ldo3_17_volt_table),
+ PM800_LDO(LDO15, LDO_ENA1_2, 6, 300000, ldo3_17_volt_table),
+ PM800_LDO(LDO16, LDO_ENA1_2, 7, 300000, ldo3_17_volt_table),
+ PM800_LDO(LDO17, LDO_ENA1_3, 0, 300000, ldo3_17_volt_table),
+ PM800_LDO(LDO18, LDO_ENA1_3, 1, 200000, ldo18_19_volt_table),
+ PM800_LDO(LDO19, LDO_ENA1_3, 2, 200000, ldo18_19_volt_table),
+};
+
+#define PM800_REGULATOR_OF_MATCH(_name, _id) \
+ [PM800_ID_##_id] = { \
+ .name = #_name, \
+ .driver_data = &pm800_regulator_info[PM800_ID_##_id], \
+ }
+
+static struct of_regulator_match pm800_regulator_matches[] = {
+ PM800_REGULATOR_OF_MATCH(buck1, BUCK1),
+ PM800_REGULATOR_OF_MATCH(buck2, BUCK2),
+ PM800_REGULATOR_OF_MATCH(buck3, BUCK3),
+ PM800_REGULATOR_OF_MATCH(buck4, BUCK4),
+ PM800_REGULATOR_OF_MATCH(buck5, BUCK5),
+ PM800_REGULATOR_OF_MATCH(ldo1, LDO1),
+ PM800_REGULATOR_OF_MATCH(ldo2, LDO2),
+ PM800_REGULATOR_OF_MATCH(ldo3, LDO3),
+ PM800_REGULATOR_OF_MATCH(ldo4, LDO4),
+ PM800_REGULATOR_OF_MATCH(ldo5, LDO5),
+ PM800_REGULATOR_OF_MATCH(ldo6, LDO6),
+ PM800_REGULATOR_OF_MATCH(ldo7, LDO7),
+ PM800_REGULATOR_OF_MATCH(ldo8, LDO8),
+ PM800_REGULATOR_OF_MATCH(ldo9, LDO9),
+ PM800_REGULATOR_OF_MATCH(ldo10, LDO10),
+ PM800_REGULATOR_OF_MATCH(ldo11, LDO11),
+ PM800_REGULATOR_OF_MATCH(ldo12, LDO12),
+ PM800_REGULATOR_OF_MATCH(ldo13, LDO13),
+ PM800_REGULATOR_OF_MATCH(ldo14, LDO14),
+ PM800_REGULATOR_OF_MATCH(ldo15, LDO15),
+ PM800_REGULATOR_OF_MATCH(ldo16, LDO16),
+ PM800_REGULATOR_OF_MATCH(ldo17, LDO17),
+ PM800_REGULATOR_OF_MATCH(ldo18, LDO18),
+ PM800_REGULATOR_OF_MATCH(ldo19, LDO19),
+};
+
+static int pm800_regulator_dt_init(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ int ret;
+
+ ret = of_regulator_match(&pdev->dev, np,
+ pm800_regulator_matches,
+ ARRAY_SIZE(pm800_regulator_matches));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int pm800_regulator_probe(struct platform_device *pdev)
+{
+ struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+ struct pm80x_platform_data *pdata = dev_get_platdata(pdev->dev.parent);
+ struct pm800_regulators *pm800_data;
+ struct pm800_regulator_info *info;
+ struct regulator_config config = { };
+ struct regulator_init_data *init_data;
+ int i, ret;
+
+ if (!pdata || pdata->num_regulators == 0) {
+ if (IS_ENABLED(CONFIG_OF)) {
+ ret = pm800_regulator_dt_init(pdev);
+ if (ret)
+ return ret;
+ } else {
+ return -ENODEV;
+ }
+ } else if (pdata->num_regulators) {
+ unsigned int count = 0;
+
+ /* Check whether num_regulator is valid. */
+ for (i = 0; i < ARRAY_SIZE(pdata->regulators); i++) {
+ if (pdata->regulators[i])
+ count++;
+ }
+ if (count != pdata->num_regulators)
+ return -EINVAL;
+ } else {
+ return -EINVAL;
+ }
+
+ pm800_data = devm_kzalloc(&pdev->dev, sizeof(*pm800_data),
+ GFP_KERNEL);
+ if (!pm800_data) {
+ dev_err(&pdev->dev, "Failed to allocate pm800_regualtors");
+ return -ENOMEM;
+ }
+
+ pm800_data->map = chip->subchip->regmap_power;
+ pm800_data->chip = chip;
+
+ platform_set_drvdata(pdev, pm800_data);
+
+ for (i = 0; i < PM800_ID_RG_MAX; i++) {
+ if (!pdata || pdata->num_regulators == 0)
+ init_data = pm800_regulator_matches[i].init_data;
+ else
+ init_data = pdata->regulators[i];
+ if (!init_data)
+ continue;
+ info = pm800_regulator_matches[i].driver_data;
+ config.dev = &pdev->dev;
+ config.init_data = init_data;
+ config.driver_data = info;
+ config.regmap = pm800_data->map;
+ config.of_node = pm800_regulator_matches[i].of_node;
+
+ pm800_data->regulators[i] =
+ regulator_register(&info->desc, &config);
+ if (IS_ERR(pm800_data->regulators[i])) {
+ ret = PTR_ERR(pm800_data->regulators[i]);
+ dev_err(&pdev->dev, "Failed to register %s\n",
+ info->desc.name);
+
+ while (--i >= 0)
+ regulator_unregister(pm800_data->regulators[i]);
+
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int pm800_regulator_remove(struct platform_device *pdev)
+{
+ struct pm800_regulators *pm800_data = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < PM800_ID_RG_MAX; i++)
+ regulator_unregister(pm800_data->regulators[i]);
+
+ return 0;
+}
+
+static struct platform_driver pm800_regulator_driver = {
+ .driver = {
+ .name = "88pm80x-regulator",
+ .owner = THIS_MODULE,
+ },
+ .probe = pm800_regulator_probe,
+ .remove = pm800_regulator_remove,
+};
+
+module_platform_driver(pm800_regulator_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Joseph(Yossi) Hanin <yhanin@marvell.com>");
+MODULE_DESCRIPTION("Regulator Driver for Marvell 88PM800 PMIC");
+MODULE_ALIAS("platform:88pm800-regulator");
{
struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
struct pm8607_regulator_info *info = NULL;
- struct regulator_init_data *pdata = pdev->dev.platform_data;
+ struct regulator_init_data *pdata = dev_get_platdata(&pdev->dev);
struct regulator_config config = { };
struct resource *res;
int i;
If unsure, say no.
-config REGULATOR_GPIO
- tristate "GPIO regulator support"
- depends on GPIOLIB
+config REGULATOR_88PM800
+ tristate "Marvell 88PM800 Power regulators"
+ depends on MFD_88PM800
help
- This driver provides support for regulators that can be
- controlled via gpios.
- It is capable of supporting current and voltage regulators
- and the platform has to provide a mapping of GPIO-states
- to target volts/amps.
+ This driver supports Marvell 88PM800 voltage regulator chips.
+ It delivers digitally programmable output,
+ the voltage is programmed via I2C interface.
+ It's suitable to support PXA988 chips to control VCC_MAIN and
+ various voltages.
+
+config REGULATOR_88PM8607
+ tristate "Marvell 88PM8607 Power regulators"
+ depends on MFD_88PM860X=y
+ help
+ This driver supports 88PM8607 voltage regulator chips.
config REGULATOR_AD5398
tristate "Analog Devices AD5398/AD5821 regulators"
This driver supports AD5398 and AD5821 current regulator chips.
If building into module, its name is ad5398.ko.
+config REGULATOR_ANATOP
+ tristate "Freescale i.MX on-chip ANATOP LDO regulators"
+ depends on MFD_SYSCON
+ help
+ Say y here to support Freescale i.MX on-chip ANATOP LDOs
+ regulators. It is recommended that this option be
+ enabled on i.MX6 platform.
+
config REGULATOR_AAT2870
tristate "AnalogicTech AAT2870 Regulators"
depends on MFD_AAT2870_CORE
If you have a AnalogicTech AAT2870 say Y to enable the
regulator driver.
+config REGULATOR_AB3100
+ tristate "ST-Ericsson AB3100 Regulator functions"
+ depends on AB3100_CORE
+ default y if AB3100_CORE
+ help
+ These regulators correspond to functionality in the
+ AB3100 analog baseband dealing with power regulators
+ for the system.
+
+config REGULATOR_AB8500
+ bool "ST-Ericsson AB8500 Power Regulators"
+ depends on AB8500_CORE
+ help
+ This driver supports the regulators found on the ST-Ericsson mixed
+ signal AB8500 PMIC
+
config REGULATOR_ARIZONA
tristate "Wolfson Arizona class devices"
depends on MFD_ARIZONA
Support for the regulators found on Wolfson Arizona class
devices.
+config REGULATOR_AS3711
+ tristate "AS3711 PMIC"
+ depends on MFD_AS3711
+ help
+ This driver provides support for the voltage regulators on the
+ AS3711 PMIC
+
config REGULATOR_DA903X
tristate "Dialog Semiconductor DA9030/DA9034 regulators"
depends on PMIC_DA903X
This driver can also be built as a module. If so, the module
will be called da9055-regulator.
+config REGULATOR_DA9063
+ tristate "Dialog Semiconductor DA9063 regulators"
+ depends on MFD_DA9063
+ help
+ Say y here to support the BUCKs and LDOs regulators found on
+ DA9063 PMICs.
+
+ This driver can also be built as a module. If so, the module
+ will be called da9063-regulator.
+
+config REGULATOR_DA9210
+ tristate "Dialog Semiconductor DA9210 regulator"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Say y here to support for the Dialog Semiconductor DA9210.
+ The DA9210 is a multi-phase synchronous step down
+ converter 12A DC-DC Buck controlled through an I2C
+ interface.
+
+config REGULATOR_DBX500_PRCMU
+ bool
+
+config REGULATOR_DB8500_PRCMU
+ bool "ST-Ericsson DB8500 Voltage Domain Regulators"
+ depends on MFD_DB8500_PRCMU
+ select REGULATOR_DBX500_PRCMU
+ help
+ This driver supports the voltage domain regulators controlled by the
+ DB8500 PRCMU
+
config REGULATOR_FAN53555
tristate "Fairchild FAN53555 Regulator"
depends on I2C
input voltage supply of 2.5V to 5.5V. The output voltage is
programmed through an I2C interface.
-config REGULATOR_ANATOP
- tristate "Freescale i.MX on-chip ANATOP LDO regulators"
- depends on MFD_SYSCON
+config REGULATOR_GPIO
+ tristate "GPIO regulator support"
+ depends on GPIOLIB
help
- Say y here to support Freescale i.MX on-chip ANATOP LDOs
- regulators. It is recommended that this option be
- enabled on i.MX6 platform.
+ This driver provides support for regulators that can be
+ controlled via gpios.
+ It is capable of supporting current and voltage regulators
+ and the platform has to provide a mapping of GPIO-states
+ to target volts/amps.
-config REGULATOR_MC13XXX_CORE
- tristate
+config REGULATOR_ISL6271A
+ tristate "Intersil ISL6271A Power regulator"
+ depends on I2C
+ help
+ This driver supports ISL6271A voltage regulator chip.
-config REGULATOR_MC13783
- tristate "Freescale MC13783 regulator driver"
- depends on MFD_MC13783
- select REGULATOR_MC13XXX_CORE
+config REGULATOR_LP3971
+ tristate "National Semiconductors LP3971 PMIC regulator driver"
+ depends on I2C
help
- Say y here to support the regulators found on the Freescale MC13783
- PMIC.
+ Say Y here to support the voltage regulators and convertors
+ on National Semiconductors LP3971 PMIC
-config REGULATOR_MC13892
- tristate "Freescale MC13892 regulator driver"
- depends on MFD_MC13XXX
- select REGULATOR_MC13XXX_CORE
+config REGULATOR_LP3972
+ tristate "National Semiconductors LP3972 PMIC regulator driver"
+ depends on I2C
help
- Say y here to support the regulators found on the Freescale MC13892
- PMIC.
+ Say Y here to support the voltage regulators and convertors
+ on National Semiconductors LP3972 PMIC
-config REGULATOR_ISL6271A
- tristate "Intersil ISL6271A Power regulator"
+config REGULATOR_LP872X
+ tristate "TI/National Semiconductor LP8720/LP8725 voltage regulators"
depends on I2C
+ select REGMAP_I2C
help
- This driver supports ISL6271A voltage regulator chip.
+ This driver supports LP8720/LP8725 PMIC
-config REGULATOR_88PM8607
- bool "Marvell 88PM8607 Power regulators"
- depends on MFD_88PM860X=y
+config REGULATOR_LP8755
+ tristate "TI LP8755 High Performance PMU driver"
+ depends on I2C
+ select REGMAP_I2C
help
- This driver supports 88PM8607 voltage regulator chips.
+ This driver supports LP8755 High Performance PMU driver. This
+ chip contains six step-down DC/DC converters which can support
+ 9 mode multiphase configuration.
+
+config REGULATOR_LP8788
+ tristate "TI LP8788 Power Regulators"
+ depends on MFD_LP8788
+ help
+ This driver supports LP8788 voltage regulator chip.
config REGULATOR_MAX1586
tristate "Maxim 1586/1587 voltage regulator"
and one current regulator 'CHARGER'. This is suitable for
Exynos-4x12 chips.
-config REGULATOR_PCAP
- tristate "Motorola PCAP2 regulator driver"
- depends on EZX_PCAP
- help
- This driver provides support for the voltage regulators of the
- PCAP2 PMIC.
+config REGULATOR_MC13XXX_CORE
+ tristate
-config REGULATOR_LP3971
- tristate "National Semiconductors LP3971 PMIC regulator driver"
- depends on I2C
+config REGULATOR_MC13783
+ tristate "Freescale MC13783 regulator driver"
+ depends on MFD_MC13783
+ select REGULATOR_MC13XXX_CORE
help
- Say Y here to support the voltage regulators and convertors
- on National Semiconductors LP3971 PMIC
+ Say y here to support the regulators found on the Freescale MC13783
+ PMIC.
-config REGULATOR_LP3972
- tristate "National Semiconductors LP3972 PMIC regulator driver"
- depends on I2C
+config REGULATOR_MC13892
+ tristate "Freescale MC13892 regulator driver"
+ depends on MFD_MC13XXX
+ select REGULATOR_MC13XXX_CORE
help
- Say Y here to support the voltage regulators and convertors
- on National Semiconductors LP3972 PMIC
+ Say y here to support the regulators found on the Freescale MC13892
+ PMIC.
-config REGULATOR_LP872X
- bool "TI/National Semiconductor LP8720/LP8725 voltage regulators"
- depends on I2C=y
- select REGMAP_I2C
+config REGULATOR_PALMAS
+ tristate "TI Palmas PMIC Regulators"
+ depends on MFD_PALMAS
help
- This driver supports LP8720/LP8725 PMIC
+ If you wish to control the regulators on the Palmas series of
+ chips say Y here. This will enable support for all the software
+ controllable SMPS/LDO regulators.
-config REGULATOR_LP8755
- tristate "TI LP8755 High Performance PMU driver"
- depends on I2C
- select REGMAP_I2C
- help
- This driver supports LP8755 High Performance PMU driver. This
- chip contains six step-down DC/DC converters which can support
- 9 mode multiphase configuration.
+ The regulators available on Palmas series chips vary depending
+ on the muxing. This is handled automatically in the driver by
+ reading the mux info from OTP.
-config REGULATOR_LP8788
- bool "TI LP8788 Power Regulators"
- depends on MFD_LP8788
+config REGULATOR_PCAP
+ tristate "Motorola PCAP2 regulator driver"
+ depends on EZX_PCAP
help
- This driver supports LP8788 voltage regulator chip.
+ This driver provides support for the voltage regulators of the
+ PCAP2 PMIC.
config REGULATOR_PCF50633
tristate "NXP PCF50633 regulator driver"
Say Y here to support the voltage regulators and convertors
on PCF50633
+config REGULATOR_PFUZE100
+ tristate "Support regulators on Freescale PFUZE100 PMIC"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Say y here to support the regulators found on the Freescale PFUZE100
+ PMIC.
+
config REGULATOR_RC5T583
tristate "RICOH RC5T583 Power regulators"
depends on MFD_RC5T583
via I2C bus. S5M8767A have 9 Bucks and 28 LDOs output and
supports DVS mode with 8bits of output voltage control.
-config REGULATOR_AB3100
- tristate "ST-Ericsson AB3100 Regulator functions"
- depends on AB3100_CORE
- default y if AB3100_CORE
- help
- These regulators correspond to functionality in the
- AB3100 analog baseband dealing with power regulators
- for the system.
-
-config REGULATOR_AB8500
- bool "ST-Ericsson AB8500 Power Regulators"
- depends on AB8500_CORE
- help
- This driver supports the regulators found on the ST-Ericsson mixed
- signal AB8500 PMIC
-
-config REGULATOR_DBX500_PRCMU
- bool
-
-config REGULATOR_DB8500_PRCMU
- bool "ST-Ericsson DB8500 Voltage Domain Regulators"
- depends on MFD_DB8500_PRCMU
- select REGULATOR_DBX500_PRCMU
- help
- This driver supports the voltage domain regulators controlled by the
- DB8500 PRCMU
-
-config REGULATOR_PALMAS
- tristate "TI Palmas PMIC Regulators"
- depends on MFD_PALMAS
+config REGULATOR_TI_ABB
+ tristate "TI Adaptive Body Bias on-chip LDO"
+ depends on ARCH_OMAP
help
- If you wish to control the regulators on the Palmas series of
- chips say Y here. This will enable support for all the software
- controllable SMPS/LDO regulators.
-
- The regulators available on Palmas series chips vary depending
- on the muxing. This is handled automatically in the driver by
- reading the mux info from OTP.
+ Select this option to support Texas Instruments' on-chip Adaptive Body
+ Bias (ABB) LDO regulators. It is recommended that this option be
+ enabled on required TI SoC. Certain Operating Performance Points
+ on TI SoCs may be unstable without enabling this as it provides
+ device specific optimized bias to allow/optimize functionality.
config REGULATOR_TPS51632
tristate "TI TPS51632 Power Regulator"
output to control regulators.
config REGULATOR_TWL4030
- bool "TI TWL4030/TWL5030/TWL6030/TPS659x0 PMIC"
+ tristate "TI TWL4030/TWL5030/TWL6030/TPS659x0 PMIC"
depends on TWL4030_CORE
help
This driver supports the voltage regulators provided by
this family of companion chips.
-config REGULATOR_TI_ABB
- bool "TI Adaptive Body Bias on-chip LDO"
- depends on ARCH_OMAP
- help
- Select this option to support Texas Instruments' on-chip Adaptive Body
- Bias (ABB) LDO regulators. It is recommended that this option be
- enabled on required TI SoC. Certain Operating Performance Points
- on TI SoCs may be unstable without enabling this as it provides
- device specific optimized bias to allow/optimize functionality.
-
config REGULATOR_VEXPRESS
tristate "Versatile Express regulators"
depends on VEXPRESS_CONFIG
This driver provides support for the voltage regulators on the
WM8994 CODEC.
-config REGULATOR_AS3711
- tristate "AS3711 PMIC"
- depends on MFD_AS3711
- help
- This driver provides support for the voltage regulators on the
- AS3711 PMIC
-
endif
#
-obj-$(CONFIG_REGULATOR) += core.o dummy.o fixed-helper.o
+obj-$(CONFIG_REGULATOR) += core.o dummy.o fixed-helper.o helpers.o
obj-$(CONFIG_OF) += of_regulator.o
obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o
obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o
obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o
+obj-$(CONFIG_REGULATOR_88PM800) += 88pm800.o
obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o
obj-$(CONFIG_REGULATOR_AAT2870) += aat2870-regulator.o
obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o
obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o
obj-$(CONFIG_REGULATOR_DA9055) += da9055-regulator.o
+obj-$(CONFIG_REGULATOR_DA9063) += da9063-regulator.o
+obj-$(CONFIG_REGULATOR_DA9210) += da9210-regulator.o
obj-$(CONFIG_REGULATOR_DBX500_PRCMU) += dbx500-prcmu.o
obj-$(CONFIG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o
obj-$(CONFIG_REGULATOR_FAN53555) += fan53555.o
obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o
obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
+obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o
obj-$(CONFIG_REGULATOR_TPS51632) += tps51632-regulator.o
obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o
obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o
obj-$(CONFIG_REGULATOR_RC5T583) += rc5t583-regulator.o
obj-$(CONFIG_REGULATOR_S2MPS11) += s2mps11.o
obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o
+obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o
obj-$(CONFIG_REGULATOR_TPS6105X) += tps6105x-regulator.o
obj-$(CONFIG_REGULATOR_TPS62360) += tps62360-regulator.o
obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o
obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o
obj-$(CONFIG_REGULATOR_TPS80031) += tps80031-regulator.o
obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o
-obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o
obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o
config.dev = &pdev->dev;
config.driver_data = ri;
- config.init_data = pdev->dev.platform_data;
+ config.init_data = dev_get_platdata(&pdev->dev);
rdev = regulator_register(&ri->desc, &config);
if (IS_ERR(rdev)) {
static int ab3100_regulators_probe(struct platform_device *pdev)
{
- struct ab3100_platform_data *plfdata = pdev->dev.platform_data;
+ struct ab3100_platform_data *plfdata = dev_get_platdata(&pdev->dev);
struct device_node *np = pdev->dev.of_node;
int err = 0;
u8 data;
static int ad5398_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- struct regulator_init_data *init_data = client->dev.platform_data;
+ struct regulator_init_data *init_data = dev_get_platdata(&client->dev);
struct regulator_config config = { };
struct ad5398_chip_info *chip;
const struct ad5398_current_data_format *df =
struct regulator_dev *rdev;
};
-static int as3711_list_voltage_sd(struct regulator_dev *rdev,
- unsigned int selector)
-{
- if (selector >= rdev->desc->n_voltages)
- return -EINVAL;
-
- if (!selector)
- return 0;
- if (selector < 0x41)
- return 600000 + selector * 12500;
- if (selector < 0x71)
- return 1400000 + (selector - 0x40) * 25000;
- return 2600000 + (selector - 0x70) * 50000;
-}
-
-static int as3711_list_voltage_aldo(struct regulator_dev *rdev,
- unsigned int selector)
-{
- if (selector >= rdev->desc->n_voltages)
- return -EINVAL;
-
- if (selector < 0x10)
- return 1200000 + selector * 50000;
- return 1800000 + (selector - 0x10) * 100000;
-}
-
-static int as3711_list_voltage_dldo(struct regulator_dev *rdev,
- unsigned int selector)
-{
- if (selector >= rdev->desc->n_voltages ||
- (selector > 0x10 && selector < 0x20))
- return -EINVAL;
-
- if (selector < 0x11)
- return 900000 + selector * 50000;
- return 1750000 + (selector - 0x20) * 50000;
-}
-
-static int as3711_bound_check(struct regulator_dev *rdev,
- int *min_uV, int *max_uV)
-{
- struct as3711_regulator *reg = rdev_get_drvdata(rdev);
- struct as3711_regulator_info *info = reg->reg_info;
-
- dev_dbg(&rdev->dev, "%s(), %d, %d, %d\n", __func__,
- *min_uV, rdev->desc->min_uV, info->max_uV);
-
- if (*max_uV < *min_uV ||
- *min_uV > info->max_uV || rdev->desc->min_uV > *max_uV)
- return -EINVAL;
-
- if (rdev->desc->n_voltages == 1)
- return 0;
-
- if (*max_uV > info->max_uV)
- *max_uV = info->max_uV;
-
- if (*min_uV < rdev->desc->min_uV)
- *min_uV = rdev->desc->min_uV;
-
- return *min_uV;
-}
-
-static int as3711_sel_check(int min, int max, int bottom, int step)
-{
- int sel, voltage;
-
- /* Round up min, when dividing: keeps us within the range */
- sel = DIV_ROUND_UP(min - bottom, step);
- voltage = sel * step + bottom;
- pr_debug("%s(): select %d..%d in %d+N*%d: %d\n", __func__,
- min, max, bottom, step, sel);
- if (voltage > max)
- return -EINVAL;
-
- return sel;
-}
-
-static int as3711_map_voltage_sd(struct regulator_dev *rdev,
- int min_uV, int max_uV)
-{
- int ret;
-
- ret = as3711_bound_check(rdev, &min_uV, &max_uV);
- if (ret <= 0)
- return ret;
-
- if (min_uV <= 1400000)
- return as3711_sel_check(min_uV, max_uV, 600000, 12500);
-
- if (min_uV <= 2600000)
- return as3711_sel_check(min_uV, max_uV, 1400000, 25000) + 0x40;
-
- return as3711_sel_check(min_uV, max_uV, 2600000, 50000) + 0x70;
-}
-
/*
* The regulator API supports 4 modes of operataion: FAST, NORMAL, IDLE and
* STANDBY. We map them in the following way to AS3711 SD1-4 DCDC modes:
return -EINVAL;
}
-static int as3711_map_voltage_aldo(struct regulator_dev *rdev,
- int min_uV, int max_uV)
-{
- int ret;
-
- ret = as3711_bound_check(rdev, &min_uV, &max_uV);
- if (ret <= 0)
- return ret;
-
- if (min_uV <= 1800000)
- return as3711_sel_check(min_uV, max_uV, 1200000, 50000);
-
- return as3711_sel_check(min_uV, max_uV, 1800000, 100000) + 0x10;
-}
-
-static int as3711_map_voltage_dldo(struct regulator_dev *rdev,
- int min_uV, int max_uV)
-{
- int ret;
-
- ret = as3711_bound_check(rdev, &min_uV, &max_uV);
- if (ret <= 0)
- return ret;
-
- if (min_uV <= 1700000)
- return as3711_sel_check(min_uV, max_uV, 900000, 50000);
-
- return as3711_sel_check(min_uV, max_uV, 1750000, 50000) + 0x20;
-}
-
static struct regulator_ops as3711_sd_ops = {
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
- .list_voltage = as3711_list_voltage_sd,
- .map_voltage = as3711_map_voltage_sd,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
.get_mode = as3711_get_mode_sd,
.set_mode = as3711_set_mode_sd,
};
.disable = regulator_disable_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
- .list_voltage = as3711_list_voltage_aldo,
- .map_voltage = as3711_map_voltage_aldo,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
};
static struct regulator_ops as3711_dldo_ops = {
.disable = regulator_disable_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
- .list_voltage = as3711_list_voltage_dldo,
- .map_voltage = as3711_map_voltage_dldo,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+};
+
+static const struct regulator_linear_range as3711_sd_ranges[] = {
+ { .min_uV = 612500, .max_uV = 1400000,
+ .min_sel = 0x1, .max_sel = 0x40, .uV_step = 12500 },
+ { .min_uV = 1425000, .max_uV = 2600000,
+ .min_sel = 0x41, .max_sel = 0x70, .uV_step = 25000 },
+ { .min_uV = 2650000, .max_uV = 3350000,
+ .min_sel = 0x71, .max_sel = 0x7f, .uV_step = 50000 },
+};
+
+static const struct regulator_linear_range as3711_aldo_ranges[] = {
+ { .min_uV = 1200000, .max_uV = 1950000,
+ .min_sel = 0, .max_sel = 0xf, .uV_step = 50000 },
+ { .min_uV = 1800000, .max_uV = 3300000,
+ .min_sel = 0x10, .max_sel = 0x1f, .uV_step = 100000 },
+};
+
+static const struct regulator_linear_range as3711_dldo_ranges[] = {
+ { .min_uV = 900000, .max_uV = 1700000,
+ .min_sel = 0, .max_sel = 0x10, .uV_step = 50000 },
+ { .min_uV = 1750000, .max_uV = 3300000,
+ .min_sel = 0x20, .max_sel = 0x3f, .uV_step = 50000 },
};
#define AS3711_REG(_id, _en_reg, _en_bit, _vmask, _vshift, _min_uV, _max_uV, _sfx) \
.enable_reg = AS3711_ ## _en_reg, \
.enable_mask = BIT(_en_bit), \
.min_uV = _min_uV, \
+ .linear_ranges = as3711_ ## _sfx ## _ranges, \
+ .n_linear_ranges = ARRAY_SIZE(as3711_ ## _sfx ## _ranges), \
}, \
.max_uV = _max_uV, \
}
}
static DEVICE_ATTR(microamps, 0444, regulator_uA_show, NULL);
-static ssize_t regulator_name_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t name_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct regulator_dev *rdev = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", rdev_get_name(rdev));
}
+static DEVICE_ATTR_RO(name);
static ssize_t regulator_print_opmode(char *buf, int mode)
{
}
static DEVICE_ATTR(requested_microamps, 0444, regulator_total_uA_show, NULL);
-static ssize_t regulator_num_users_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t num_users_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct regulator_dev *rdev = dev_get_drvdata(dev);
return sprintf(buf, "%d\n", rdev->use_count);
}
+static DEVICE_ATTR_RO(num_users);
-static ssize_t regulator_type_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t type_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct regulator_dev *rdev = dev_get_drvdata(dev);
}
return sprintf(buf, "unknown\n");
}
+static DEVICE_ATTR_RO(type);
static ssize_t regulator_suspend_mem_uV_show(struct device *dev,
struct device_attribute *attr, char *buf)
* These are the only attributes are present for all regulators.
* Other attributes are a function of regulator functionality.
*/
-static struct device_attribute regulator_dev_attrs[] = {
- __ATTR(name, 0444, regulator_name_show, NULL),
- __ATTR(num_users, 0444, regulator_num_users_show, NULL),
- __ATTR(type, 0444, regulator_type_show, NULL),
- __ATTR_NULL,
+static struct attribute *regulator_dev_attrs[] = {
+ &dev_attr_name.attr,
+ &dev_attr_num_users.attr,
+ &dev_attr_type.attr,
+ NULL,
};
+ATTRIBUTE_GROUPS(regulator_dev);
static void regulator_dev_release(struct device *dev)
{
static struct class regulator_class = {
.name = "regulator",
.dev_release = regulator_dev_release,
- .dev_attrs = regulator_dev_attrs,
+ .dev_groups = regulator_dev_groups,
};
/* Calculate the new optimum regulator operating mode based on the new total
}
}
- if (rdev->constraints->ramp_delay && ops->set_ramp_delay) {
+ if ((rdev->constraints->ramp_delay || rdev->constraints->ramp_disable)
+ && ops->set_ramp_delay) {
ret = ops->set_ramp_delay(rdev, rdev->constraints->ramp_delay);
if (ret < 0) {
rdev_err(rdev, "failed to set ramp_delay\n");
/* Internal regulator request function */
static struct regulator *_regulator_get(struct device *dev, const char *id,
- int exclusive)
+ bool exclusive)
{
struct regulator_dev *rdev;
struct regulator *regulator = ERR_PTR(-EPROBE_DEFER);
*/
struct regulator *regulator_get(struct device *dev, const char *id)
{
- return _regulator_get(dev, id, 0);
+ return _regulator_get(dev, id, false);
}
EXPORT_SYMBOL_GPL(regulator_get);
*/
struct regulator *regulator_get_exclusive(struct device *dev, const char *id)
{
- return _regulator_get(dev, id, 1);
+ return _regulator_get(dev, id, true);
}
EXPORT_SYMBOL_GPL(regulator_get_exclusive);
+/**
+ * regulator_get_optional - obtain optional access to a regulator.
+ * @dev: device for regulator "consumer"
+ * @id: Supply name or regulator ID.
+ *
+ * Returns a struct regulator corresponding to the regulator producer,
+ * or IS_ERR() condition containing errno. Other consumers will be
+ * unable to obtain this reference is held and the use count for the
+ * regulator will be initialised to reflect the current state of the
+ * regulator.
+ *
+ * This is intended for use by consumers for devices which can have
+ * some supplies unconnected in normal use, such as some MMC devices.
+ * It can allow the regulator core to provide stub supplies for other
+ * supplies requested using normal regulator_get() calls without
+ * disrupting the operation of drivers that can handle absent
+ * supplies.
+ *
+ * Use of supply names configured via regulator_set_device_supply() is
+ * strongly encouraged. It is recommended that the supply name used
+ * should match the name used for the supply and/or the relevant
+ * device pins in the datasheet.
+ */
+struct regulator *regulator_get_optional(struct device *dev, const char *id)
+{
+ return _regulator_get(dev, id, 0);
+}
+EXPORT_SYMBOL_GPL(regulator_get_optional);
+
+/**
+ * devm_regulator_get_optional - Resource managed regulator_get_optional()
+ * @dev: device for regulator "consumer"
+ * @id: Supply name or regulator ID.
+ *
+ * Managed regulator_get_optional(). Regulators returned from this
+ * function are automatically regulator_put() on driver detach. See
+ * regulator_get_optional() for more information.
+ */
+struct regulator *devm_regulator_get_optional(struct device *dev,
+ const char *id)
+{
+ struct regulator **ptr, *regulator;
+
+ ptr = devres_alloc(devm_regulator_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ regulator = regulator_get_optional(dev, id);
+ if (!IS_ERR(regulator)) {
+ *ptr = regulator;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return regulator;
+}
+EXPORT_SYMBOL_GPL(devm_regulator_get_optional);
+
/* Locks held by regulator_put() */
static void _regulator_put(struct regulator *regulator)
{
module_put(rdev->owner);
}
+/**
+ * devm_regulator_get_exclusive - Resource managed regulator_get_exclusive()
+ * @dev: device for regulator "consumer"
+ * @id: Supply name or regulator ID.
+ *
+ * Managed regulator_get_exclusive(). Regulators returned from this function
+ * are automatically regulator_put() on driver detach. See regulator_get() for
+ * more information.
+ */
+struct regulator *devm_regulator_get_exclusive(struct device *dev,
+ const char *id)
+{
+ struct regulator **ptr, *regulator;
+
+ ptr = devres_alloc(devm_regulator_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ regulator = _regulator_get(dev, id, 1);
+ if (!IS_ERR(regulator)) {
+ *ptr = regulator;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return regulator;
+}
+EXPORT_SYMBOL_GPL(devm_regulator_get_exclusive);
+
/**
* regulator_put - "free" the regulator source
* @regulator: regulator source
rdev->deferred_disables++;
mutex_unlock(&rdev->mutex);
- ret = schedule_delayed_work(&rdev->disable_work,
- msecs_to_jiffies(ms));
+ ret = queue_delayed_work(system_power_efficient_wq,
+ &rdev->disable_work,
+ msecs_to_jiffies(ms));
if (ret < 0)
return ret;
else
}
EXPORT_SYMBOL_GPL(regulator_disable_deferred);
-/**
- * regulator_is_enabled_regmap - standard is_enabled() for regmap users
- *
- * @rdev: regulator to operate on
- *
- * Regulators that use regmap for their register I/O can set the
- * enable_reg and enable_mask fields in their descriptor and then use
- * this as their is_enabled operation, saving some code.
- */
-int regulator_is_enabled_regmap(struct regulator_dev *rdev)
-{
- unsigned int val;
- int ret;
-
- ret = regmap_read(rdev->regmap, rdev->desc->enable_reg, &val);
- if (ret != 0)
- return ret;
-
- if (rdev->desc->enable_is_inverted)
- return (val & rdev->desc->enable_mask) == 0;
- else
- return (val & rdev->desc->enable_mask) != 0;
-}
-EXPORT_SYMBOL_GPL(regulator_is_enabled_regmap);
-
-/**
- * regulator_enable_regmap - standard enable() for regmap users
- *
- * @rdev: regulator to operate on
- *
- * Regulators that use regmap for their register I/O can set the
- * enable_reg and enable_mask fields in their descriptor and then use
- * this as their enable() operation, saving some code.
- */
-int regulator_enable_regmap(struct regulator_dev *rdev)
-{
- unsigned int val;
-
- if (rdev->desc->enable_is_inverted)
- val = 0;
- else
- val = rdev->desc->enable_mask;
-
- return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
- rdev->desc->enable_mask, val);
-}
-EXPORT_SYMBOL_GPL(regulator_enable_regmap);
-
-/**
- * regulator_disable_regmap - standard disable() for regmap users
- *
- * @rdev: regulator to operate on
- *
- * Regulators that use regmap for their register I/O can set the
- * enable_reg and enable_mask fields in their descriptor and then use
- * this as their disable() operation, saving some code.
- */
-int regulator_disable_regmap(struct regulator_dev *rdev)
-{
- unsigned int val;
-
- if (rdev->desc->enable_is_inverted)
- val = rdev->desc->enable_mask;
- else
- val = 0;
-
- return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
- rdev->desc->enable_mask, val);
-}
-EXPORT_SYMBOL_GPL(regulator_disable_regmap);
-
static int _regulator_is_enabled(struct regulator_dev *rdev)
{
/* A GPIO control always takes precedence */
}
EXPORT_SYMBOL_GPL(regulator_count_voltages);
-/**
- * regulator_list_voltage_linear - List voltages with simple calculation
- *
- * @rdev: Regulator device
- * @selector: Selector to convert into a voltage
- *
- * Regulators with a simple linear mapping between voltages and
- * selectors can set min_uV and uV_step in the regulator descriptor
- * and then use this function as their list_voltage() operation,
- */
-int regulator_list_voltage_linear(struct regulator_dev *rdev,
- unsigned int selector)
-{
- if (selector >= rdev->desc->n_voltages)
- return -EINVAL;
- if (selector < rdev->desc->linear_min_sel)
- return 0;
-
- selector -= rdev->desc->linear_min_sel;
-
- return rdev->desc->min_uV + (rdev->desc->uV_step * selector);
-}
-EXPORT_SYMBOL_GPL(regulator_list_voltage_linear);
-
-/**
- * regulator_list_voltage_table - List voltages with table based mapping
- *
- * @rdev: Regulator device
- * @selector: Selector to convert into a voltage
- *
- * Regulators with table based mapping between voltages and
- * selectors can set volt_table in the regulator descriptor
- * and then use this function as their list_voltage() operation.
- */
-int regulator_list_voltage_table(struct regulator_dev *rdev,
- unsigned int selector)
-{
- if (!rdev->desc->volt_table) {
- BUG_ON(!rdev->desc->volt_table);
- return -EINVAL;
- }
-
- if (selector >= rdev->desc->n_voltages)
- return -EINVAL;
-
- return rdev->desc->volt_table[selector];
-}
-EXPORT_SYMBOL_GPL(regulator_list_voltage_table);
-
/**
* regulator_list_voltage - enumerate supported voltages
* @regulator: regulator source
}
EXPORT_SYMBOL_GPL(regulator_is_supported_voltage);
-/**
- * regulator_get_voltage_sel_regmap - standard get_voltage_sel for regmap users
- *
- * @rdev: regulator to operate on
- *
- * Regulators that use regmap for their register I/O can set the
- * vsel_reg and vsel_mask fields in their descriptor and then use this
- * as their get_voltage_vsel operation, saving some code.
- */
-int regulator_get_voltage_sel_regmap(struct regulator_dev *rdev)
-{
- unsigned int val;
- int ret;
-
- ret = regmap_read(rdev->regmap, rdev->desc->vsel_reg, &val);
- if (ret != 0)
- return ret;
-
- val &= rdev->desc->vsel_mask;
- val >>= ffs(rdev->desc->vsel_mask) - 1;
-
- return val;
-}
-EXPORT_SYMBOL_GPL(regulator_get_voltage_sel_regmap);
-
-/**
- * regulator_set_voltage_sel_regmap - standard set_voltage_sel for regmap users
- *
- * @rdev: regulator to operate on
- * @sel: Selector to set
- *
- * Regulators that use regmap for their register I/O can set the
- * vsel_reg and vsel_mask fields in their descriptor and then use this
- * as their set_voltage_vsel operation, saving some code.
- */
-int regulator_set_voltage_sel_regmap(struct regulator_dev *rdev, unsigned sel)
-{
- int ret;
-
- sel <<= ffs(rdev->desc->vsel_mask) - 1;
-
- ret = regmap_update_bits(rdev->regmap, rdev->desc->vsel_reg,
- rdev->desc->vsel_mask, sel);
- if (ret)
- return ret;
-
- if (rdev->desc->apply_bit)
- ret = regmap_update_bits(rdev->regmap, rdev->desc->apply_reg,
- rdev->desc->apply_bit,
- rdev->desc->apply_bit);
- return ret;
-}
-EXPORT_SYMBOL_GPL(regulator_set_voltage_sel_regmap);
-
-/**
- * regulator_map_voltage_iterate - map_voltage() based on list_voltage()
- *
- * @rdev: Regulator to operate on
- * @min_uV: Lower bound for voltage
- * @max_uV: Upper bound for voltage
- *
- * Drivers implementing set_voltage_sel() and list_voltage() can use
- * this as their map_voltage() operation. It will find a suitable
- * voltage by calling list_voltage() until it gets something in bounds
- * for the requested voltages.
- */
-int regulator_map_voltage_iterate(struct regulator_dev *rdev,
- int min_uV, int max_uV)
-{
- int best_val = INT_MAX;
- int selector = 0;
- int i, ret;
-
- /* Find the smallest voltage that falls within the specified
- * range.
- */
- for (i = 0; i < rdev->desc->n_voltages; i++) {
- ret = rdev->desc->ops->list_voltage(rdev, i);
- if (ret < 0)
- continue;
-
- if (ret < best_val && ret >= min_uV && ret <= max_uV) {
- best_val = ret;
- selector = i;
- }
- }
-
- if (best_val != INT_MAX)
- return selector;
- else
- return -EINVAL;
-}
-EXPORT_SYMBOL_GPL(regulator_map_voltage_iterate);
-
-/**
- * regulator_map_voltage_ascend - map_voltage() for ascendant voltage list
- *
- * @rdev: Regulator to operate on
- * @min_uV: Lower bound for voltage
- * @max_uV: Upper bound for voltage
- *
- * Drivers that have ascendant voltage list can use this as their
- * map_voltage() operation.
- */
-int regulator_map_voltage_ascend(struct regulator_dev *rdev,
- int min_uV, int max_uV)
-{
- int i, ret;
-
- for (i = 0; i < rdev->desc->n_voltages; i++) {
- ret = rdev->desc->ops->list_voltage(rdev, i);
- if (ret < 0)
- continue;
-
- if (ret > max_uV)
- break;
-
- if (ret >= min_uV && ret <= max_uV)
- return i;
- }
-
- return -EINVAL;
-}
-EXPORT_SYMBOL_GPL(regulator_map_voltage_ascend);
-
-/**
- * regulator_map_voltage_linear - map_voltage() for simple linear mappings
- *
- * @rdev: Regulator to operate on
- * @min_uV: Lower bound for voltage
- * @max_uV: Upper bound for voltage
- *
- * Drivers providing min_uV and uV_step in their regulator_desc can
- * use this as their map_voltage() operation.
- */
-int regulator_map_voltage_linear(struct regulator_dev *rdev,
- int min_uV, int max_uV)
-{
- int ret, voltage;
-
- /* Allow uV_step to be 0 for fixed voltage */
- if (rdev->desc->n_voltages == 1 && rdev->desc->uV_step == 0) {
- if (min_uV <= rdev->desc->min_uV && rdev->desc->min_uV <= max_uV)
- return 0;
- else
- return -EINVAL;
- }
-
- if (!rdev->desc->uV_step) {
- BUG_ON(!rdev->desc->uV_step);
- return -EINVAL;
- }
-
- if (min_uV < rdev->desc->min_uV)
- min_uV = rdev->desc->min_uV;
-
- ret = DIV_ROUND_UP(min_uV - rdev->desc->min_uV, rdev->desc->uV_step);
- if (ret < 0)
- return ret;
-
- ret += rdev->desc->linear_min_sel;
-
- /* Map back into a voltage to verify we're still in bounds */
- voltage = rdev->desc->ops->list_voltage(rdev, ret);
- if (voltage < min_uV || voltage > max_uV)
- return -EINVAL;
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(regulator_map_voltage_linear);
-
static int _regulator_do_set_voltage(struct regulator_dev *rdev,
int min_uV, int max_uV)
{
}
/* Call set_voltage_time_sel if successfully obtained old_selector */
- if (ret == 0 && _regulator_is_enabled(rdev) && old_selector >= 0 &&
- old_selector != selector && rdev->desc->ops->set_voltage_time_sel) {
+ if (ret == 0 && !rdev->constraints->ramp_disable && old_selector >= 0
+ && old_selector != selector) {
delay = rdev->desc->ops->set_voltage_time_sel(rdev,
old_selector, selector);
}
EXPORT_SYMBOL_GPL(regulator_set_optimum_mode);
-/**
- * regulator_set_bypass_regmap - Default set_bypass() using regmap
- *
- * @rdev: device to operate on.
- * @enable: state to set.
- */
-int regulator_set_bypass_regmap(struct regulator_dev *rdev, bool enable)
-{
- unsigned int val;
-
- if (enable)
- val = rdev->desc->bypass_mask;
- else
- val = 0;
-
- return regmap_update_bits(rdev->regmap, rdev->desc->bypass_reg,
- rdev->desc->bypass_mask, val);
-}
-EXPORT_SYMBOL_GPL(regulator_set_bypass_regmap);
-
-/**
- * regulator_get_bypass_regmap - Default get_bypass() using regmap
- *
- * @rdev: device to operate on.
- * @enable: current state.
- */
-int regulator_get_bypass_regmap(struct regulator_dev *rdev, bool *enable)
-{
- unsigned int val;
- int ret;
-
- ret = regmap_read(rdev->regmap, rdev->desc->bypass_reg, &val);
- if (ret != 0)
- return ret;
-
- *enable = val & rdev->desc->bypass_mask;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(regulator_get_bypass_regmap);
-
/**
* regulator_allow_bypass - allow the regulator to go into bypass mode
*
if (rdev == NULL)
return;
- if (rdev->supply)
+ if (rdev->supply) {
+ while (rdev->use_count--)
+ regulator_disable(rdev->supply);
regulator_put(rdev->supply);
+ }
mutex_lock(®ulator_list_mutex);
debugfs_remove_recursive(rdev->debugfs);
flush_work(&rdev->disable_work.work);
return ret;
}
-static int da9034_map_ldo12_voltage(struct regulator_dev *rdev,
- int min_uV, int max_uV)
-{
- struct da903x_regulator_info *info = rdev_get_drvdata(rdev);
- int sel;
-
- if (check_range(info, min_uV, max_uV)) {
- pr_err("invalid voltage range (%d, %d) uV\n", min_uV, max_uV);
- return -EINVAL;
- }
-
- sel = DIV_ROUND_UP(min_uV - info->desc.min_uV, info->desc.uV_step);
- sel = (sel >= 20) ? sel - 12 : ((sel > 7) ? 8 : sel);
-
- return sel;
-}
-
-static int da9034_list_ldo12_voltage(struct regulator_dev *rdev,
- unsigned selector)
-{
- struct da903x_regulator_info *info = rdev_get_drvdata(rdev);
- int volt;
-
- if (selector >= 8)
- volt = 2700000 + rdev->desc->uV_step * (selector - 8);
- else
- volt = rdev->desc->min_uV + rdev->desc->uV_step * selector;
-
- if (volt > info->max_uV)
- return -EINVAL;
-
- return volt;
-}
+static const struct regulator_linear_range da9034_ldo12_ranges[] = {
+ { .min_uV = 1700000, .max_uV = 2050000, .min_sel = 0, .max_sel = 7,
+ .uV_step = 50000 },
+ { .min_uV = 2700000, .max_uV = 3050000, .min_sel = 8, .max_sel = 15,
+ .uV_step = 50000 },
+};
static struct regulator_ops da903x_regulator_ldo_ops = {
.set_voltage_sel = da903x_set_voltage_sel,
static struct regulator_ops da9034_regulator_ldo12_ops = {
.set_voltage_sel = da903x_set_voltage_sel,
.get_voltage_sel = da903x_get_voltage_sel,
- .list_voltage = da9034_list_ldo12_voltage,
- .map_voltage = da9034_map_ldo12_voltage,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
.enable = da903x_enable,
.disable = da903x_disable,
.is_enabled = da903x_is_enabled,
if (ri->desc.id == DA9034_ID_LDO12) {
ri->desc.ops = &da9034_regulator_ldo12_ops;
ri->desc.n_voltages = 16;
+ ri->desc.linear_ranges = da9034_ldo12_ranges;
+ ri->desc.n_linear_ranges = ARRAY_SIZE(da9034_ldo12_ranges);
}
if (ri->desc.id == DA9030_ID_LDO14)
ri->desc.ops = &da9030_regulator_ldo1_15_ops;
config.dev = &pdev->dev;
- config.init_data = pdev->dev.platform_data;
+ config.init_data = dev_get_platdata(&pdev->dev);
config.driver_data = ri;
rdev = regulator_register(&ri->desc, &config);
return -ENOMEM;
da9052 = dev_get_drvdata(pdev->dev.parent);
- pdata = da9052->dev->platform_data;
+ pdata = dev_get_platdata(da9052->dev);
regulator->da9052 = da9052;
regulator->info = find_regulator_info(regulator->da9052->chip_id,
struct regulator_config config = { };
struct da9055_regulator *regulator;
struct da9055 *da9055 = dev_get_drvdata(pdev->dev.parent);
- struct da9055_pdata *pdata = da9055->dev->platform_data;
+ struct da9055_pdata *pdata = dev_get_platdata(da9055->dev);
int ret, irq;
if (pdata == NULL || pdata->regulators[pdev->id] == NULL)
--- /dev/null
+/*
+ * Regulator driver for DA9063 PMIC series
+ *
+ * Copyright 2012 Dialog Semiconductors Ltd.
+ * Copyright 2013 Philipp Zabel, Pengutronix
+ *
+ * Author: Krystian Garbaciak <krystian.garbaciak@diasemi.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.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/da9063/core.h>
+#include <linux/mfd/da9063/pdata.h>
+#include <linux/mfd/da9063/registers.h>
+
+
+/* Definition for registering regmap bit fields using a mask */
+#define BFIELD(_reg, _mask) \
+ REG_FIELD(_reg, __builtin_ffs((int)_mask) - 1, \
+ sizeof(unsigned int) * 8 - __builtin_clz((_mask)) - 1)
+
+/* Regulator capabilities and registers description */
+struct da9063_regulator_info {
+ struct regulator_desc desc;
+
+ /* Current limiting */
+ unsigned n_current_limits;
+ const int *current_limits;
+
+ /* DA9063 main register fields */
+ struct reg_field mode; /* buck mode of operation */
+ struct reg_field suspend;
+ struct reg_field sleep;
+ struct reg_field suspend_sleep;
+ unsigned int suspend_vsel_reg;
+ struct reg_field ilimit;
+
+ /* DA9063 event detection bit */
+ struct reg_field oc_event;
+};
+
+/* Macros for LDO */
+#define DA9063_LDO(chip, regl_name, min_mV, step_mV, max_mV) \
+ .desc.id = chip##_ID_##regl_name, \
+ .desc.name = __stringify(chip##_##regl_name), \
+ .desc.ops = &da9063_ldo_ops, \
+ .desc.min_uV = (min_mV) * 1000, \
+ .desc.uV_step = (step_mV) * 1000, \
+ .desc.n_voltages = (((max_mV) - (min_mV))/(step_mV) + 1), \
+ .desc.enable_reg = DA9063_REG_##regl_name##_CONT, \
+ .desc.enable_mask = DA9063_LDO_EN, \
+ .desc.vsel_reg = DA9063_REG_V##regl_name##_A, \
+ .desc.vsel_mask = DA9063_V##regl_name##_MASK, \
+ .desc.linear_min_sel = DA9063_V##regl_name##_BIAS, \
+ .sleep = BFIELD(DA9063_REG_V##regl_name##_A, DA9063_LDO_SL), \
+ .suspend_sleep = BFIELD(DA9063_REG_V##regl_name##_B, DA9063_LDO_SL), \
+ .suspend_vsel_reg = DA9063_REG_V##regl_name##_B
+
+/* Macros for voltage DC/DC converters (BUCKs) */
+#define DA9063_BUCK(chip, regl_name, min_mV, step_mV, max_mV, limits_array) \
+ .desc.id = chip##_ID_##regl_name, \
+ .desc.name = __stringify(chip##_##regl_name), \
+ .desc.ops = &da9063_buck_ops, \
+ .desc.min_uV = (min_mV) * 1000, \
+ .desc.uV_step = (step_mV) * 1000, \
+ .desc.n_voltages = ((max_mV) - (min_mV))/(step_mV) + 1, \
+ .current_limits = limits_array, \
+ .n_current_limits = ARRAY_SIZE(limits_array)
+
+#define DA9063_BUCK_COMMON_FIELDS(regl_name) \
+ .desc.enable_reg = DA9063_REG_##regl_name##_CONT, \
+ .desc.enable_mask = DA9063_BUCK_EN, \
+ .desc.vsel_reg = DA9063_REG_V##regl_name##_A, \
+ .desc.vsel_mask = DA9063_VBUCK_MASK, \
+ .desc.linear_min_sel = DA9063_VBUCK_BIAS, \
+ .sleep = BFIELD(DA9063_REG_V##regl_name##_A, DA9063_BUCK_SL), \
+ .suspend_sleep = BFIELD(DA9063_REG_V##regl_name##_B, DA9063_BUCK_SL), \
+ .suspend_vsel_reg = DA9063_REG_V##regl_name##_B, \
+ .mode = BFIELD(DA9063_REG_##regl_name##_CFG, DA9063_BUCK_MODE_MASK)
+
+/* Defines asignment of regulators info table to chip model */
+struct da9063_dev_model {
+ const struct da9063_regulator_info *regulator_info;
+ unsigned n_regulators;
+ unsigned dev_model;
+};
+
+/* Single regulator settings */
+struct da9063_regulator {
+ struct regulator_desc desc;
+ struct regulator_dev *rdev;
+ struct da9063 *hw;
+ const struct da9063_regulator_info *info;
+
+ struct regmap_field *mode;
+ struct regmap_field *suspend;
+ struct regmap_field *sleep;
+ struct regmap_field *suspend_sleep;
+ struct regmap_field *ilimit;
+};
+
+/* Encapsulates all information for the regulators driver */
+struct da9063_regulators {
+ int irq_ldo_lim;
+ int irq_uvov;
+
+ unsigned n_regulators;
+ /* Array size to be defined during init. Keep at end. */
+ struct da9063_regulator regulator[0];
+};
+
+/* BUCK modes for DA9063 */
+enum {
+ BUCK_MODE_MANUAL, /* 0 */
+ BUCK_MODE_SLEEP, /* 1 */
+ BUCK_MODE_SYNC, /* 2 */
+ BUCK_MODE_AUTO /* 3 */
+};
+
+/* Regulator operations */
+
+/* Current limits array (in uA) for BCORE1, BCORE2, BPRO.
+ Entry indexes corresponds to register values. */
+static const int da9063_buck_a_limits[] = {
+ 500000, 600000, 700000, 800000, 900000, 1000000, 1100000, 1200000,
+ 1300000, 1400000, 1500000, 1600000, 1700000, 1800000, 1900000, 2000000
+};
+
+/* Current limits array (in uA) for BMEM, BIO, BPERI.
+ Entry indexes corresponds to register values. */
+static const int da9063_buck_b_limits[] = {
+ 1500000, 1600000, 1700000, 1800000, 1900000, 2000000, 2100000, 2200000,
+ 2300000, 2400000, 2500000, 2600000, 2700000, 2800000, 2900000, 3000000
+};
+
+/* Current limits array (in uA) for merged BCORE1 and BCORE2.
+ Entry indexes corresponds to register values. */
+static const int da9063_bcores_merged_limits[] = {
+ 1000000, 1200000, 1400000, 1600000, 1800000, 2000000, 2200000, 2400000,
+ 2600000, 2800000, 3000000, 3200000, 3400000, 3600000, 3800000, 4000000
+};
+
+/* Current limits array (in uA) for merged BMEM and BIO.
+ Entry indexes corresponds to register values. */
+static const int da9063_bmem_bio_merged_limits[] = {
+ 3000000, 3200000, 3400000, 3600000, 3800000, 4000000, 4200000, 4400000,
+ 4600000, 4800000, 5000000, 5200000, 5400000, 5600000, 5800000, 6000000
+};
+
+static int da9063_set_current_limit(struct regulator_dev *rdev,
+ int min_uA, int max_uA)
+{
+ struct da9063_regulator *regl = rdev_get_drvdata(rdev);
+ const struct da9063_regulator_info *rinfo = regl->info;
+ int n, tval;
+
+ for (n = 0; n < rinfo->n_current_limits; n++) {
+ tval = rinfo->current_limits[n];
+ if (tval >= min_uA && tval <= max_uA)
+ return regmap_field_write(regl->ilimit, n);
+ }
+
+ return -EINVAL;
+}
+
+static int da9063_get_current_limit(struct regulator_dev *rdev)
+{
+ struct da9063_regulator *regl = rdev_get_drvdata(rdev);
+ const struct da9063_regulator_info *rinfo = regl->info;
+ unsigned int sel;
+ int ret;
+
+ ret = regmap_field_read(regl->ilimit, &sel);
+ if (ret < 0)
+ return ret;
+
+ if (sel >= rinfo->n_current_limits)
+ sel = rinfo->n_current_limits - 1;
+
+ return rinfo->current_limits[sel];
+}
+
+static int da9063_buck_set_mode(struct regulator_dev *rdev, unsigned mode)
+{
+ struct da9063_regulator *regl = rdev_get_drvdata(rdev);
+ unsigned val;
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ val = BUCK_MODE_SYNC;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ val = BUCK_MODE_AUTO;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = BUCK_MODE_SLEEP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_field_write(regl->mode, val);
+}
+
+/*
+ * Bucks use single mode register field for normal operation
+ * and suspend state.
+ * There are 3 modes to map to: FAST, NORMAL, and STANDBY.
+ */
+
+static unsigned da9063_buck_get_mode(struct regulator_dev *rdev)
+{
+ struct da9063_regulator *regl = rdev_get_drvdata(rdev);
+ struct regmap_field *field;
+ unsigned int val, mode = 0;
+ int ret;
+
+ ret = regmap_field_read(regl->mode, &val);
+ if (ret < 0)
+ return ret;
+
+ switch (val) {
+ default:
+ case BUCK_MODE_MANUAL:
+ mode = REGULATOR_MODE_FAST | REGULATOR_MODE_STANDBY;
+ /* Sleep flag bit decides the mode */
+ break;
+ case BUCK_MODE_SLEEP:
+ return REGULATOR_MODE_STANDBY;
+ case BUCK_MODE_SYNC:
+ return REGULATOR_MODE_FAST;
+ case BUCK_MODE_AUTO:
+ return REGULATOR_MODE_NORMAL;
+ }
+
+ /* Detect current regulator state */
+ ret = regmap_field_read(regl->suspend, &val);
+ if (ret < 0)
+ return 0;
+
+ /* Read regulator mode from proper register, depending on state */
+ if (val)
+ field = regl->suspend_sleep;
+ else
+ field = regl->sleep;
+
+ ret = regmap_field_read(field, &val);
+ if (ret < 0)
+ return 0;
+
+ if (val)
+ mode &= REGULATOR_MODE_STANDBY;
+ else
+ mode &= REGULATOR_MODE_NORMAL | REGULATOR_MODE_FAST;
+
+ return mode;
+}
+
+/*
+ * LDOs use sleep flags - one for normal and one for suspend state.
+ * There are 2 modes to map to: NORMAL and STANDBY (sleep) for each state.
+ */
+
+static int da9063_ldo_set_mode(struct regulator_dev *rdev, unsigned mode)
+{
+ struct da9063_regulator *regl = rdev_get_drvdata(rdev);
+ unsigned val;
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ val = 0;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_field_write(regl->sleep, val);
+}
+
+static unsigned da9063_ldo_get_mode(struct regulator_dev *rdev)
+{
+ struct da9063_regulator *regl = rdev_get_drvdata(rdev);
+ struct regmap_field *field;
+ int ret, val;
+
+ /* Detect current regulator state */
+ ret = regmap_field_read(regl->suspend, &val);
+ if (ret < 0)
+ return 0;
+
+ /* Read regulator mode from proper register, depending on state */
+ if (val)
+ field = regl->suspend_sleep;
+ else
+ field = regl->sleep;
+
+ ret = regmap_field_read(field, &val);
+ if (ret < 0)
+ return 0;
+
+ if (val)
+ return REGULATOR_MODE_STANDBY;
+ else
+ return REGULATOR_MODE_NORMAL;
+}
+
+static int da9063_buck_get_status(struct regulator_dev *rdev)
+{
+ int ret = regulator_is_enabled_regmap(rdev);
+
+ if (ret == 0) {
+ ret = REGULATOR_STATUS_OFF;
+ } else if (ret > 0) {
+ ret = da9063_buck_get_mode(rdev);
+ if (ret > 0)
+ ret = regulator_mode_to_status(ret);
+ else if (ret == 0)
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static int da9063_ldo_get_status(struct regulator_dev *rdev)
+{
+ int ret = regulator_is_enabled_regmap(rdev);
+
+ if (ret == 0) {
+ ret = REGULATOR_STATUS_OFF;
+ } else if (ret > 0) {
+ ret = da9063_ldo_get_mode(rdev);
+ if (ret > 0)
+ ret = regulator_mode_to_status(ret);
+ else if (ret == 0)
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static int da9063_set_suspend_voltage(struct regulator_dev *rdev, int uV)
+{
+ struct da9063_regulator *regl = rdev_get_drvdata(rdev);
+ const struct da9063_regulator_info *rinfo = regl->info;
+ int ret, sel;
+
+ sel = regulator_map_voltage_linear(rdev, uV, uV);
+ if (sel < 0)
+ return -EINVAL;
+
+ sel <<= ffs(rdev->desc->vsel_mask) - 1;
+
+ ret = regmap_update_bits(regl->hw->regmap, rinfo->suspend_vsel_reg,
+ rdev->desc->vsel_mask, sel);
+
+ return ret;
+}
+
+static int da9063_suspend_enable(struct regulator_dev *rdev)
+{
+ struct da9063_regulator *regl = rdev_get_drvdata(rdev);
+
+ return regmap_field_write(regl->suspend, 1);
+}
+
+static int da9063_suspend_disable(struct regulator_dev *rdev)
+{
+ struct da9063_regulator *regl = rdev_get_drvdata(rdev);
+
+ return regmap_field_write(regl->suspend, 0);
+}
+
+static int da9063_buck_set_suspend_mode(struct regulator_dev *rdev, unsigned mode)
+{
+ struct da9063_regulator *regl = rdev_get_drvdata(rdev);
+ int val;
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ val = BUCK_MODE_SYNC;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ val = BUCK_MODE_AUTO;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = BUCK_MODE_SLEEP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_field_write(regl->mode, val);
+}
+
+static int da9063_ldo_set_suspend_mode(struct regulator_dev *rdev, unsigned mode)
+{
+ struct da9063_regulator *regl = rdev_get_drvdata(rdev);
+ unsigned val;
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ val = 0;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_field_write(regl->suspend_sleep, val);
+}
+
+static struct regulator_ops da9063_buck_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_current_limit = da9063_set_current_limit,
+ .get_current_limit = da9063_get_current_limit,
+ .set_mode = da9063_buck_set_mode,
+ .get_mode = da9063_buck_get_mode,
+ .get_status = da9063_buck_get_status,
+ .set_suspend_voltage = da9063_set_suspend_voltage,
+ .set_suspend_enable = da9063_suspend_enable,
+ .set_suspend_disable = da9063_suspend_disable,
+ .set_suspend_mode = da9063_buck_set_suspend_mode,
+};
+
+static struct regulator_ops da9063_ldo_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_mode = da9063_ldo_set_mode,
+ .get_mode = da9063_ldo_get_mode,
+ .get_status = da9063_ldo_get_status,
+ .set_suspend_voltage = da9063_set_suspend_voltage,
+ .set_suspend_enable = da9063_suspend_enable,
+ .set_suspend_disable = da9063_suspend_disable,
+ .set_suspend_mode = da9063_ldo_set_suspend_mode,
+};
+
+/* Info of regulators for DA9063 */
+static const struct da9063_regulator_info da9063_regulator_info[] = {
+ {
+ DA9063_BUCK(DA9063, BCORE1, 300, 10, 1570,
+ da9063_buck_a_limits),
+ DA9063_BUCK_COMMON_FIELDS(BCORE1),
+ .suspend = BFIELD(DA9063_REG_DVC_1, DA9063_VBCORE1_SEL),
+ .ilimit = BFIELD(DA9063_REG_BUCK_ILIM_C,
+ DA9063_BCORE1_ILIM_MASK),
+ },
+ {
+ DA9063_BUCK(DA9063, BCORE2, 300, 10, 1570,
+ da9063_buck_a_limits),
+ DA9063_BUCK_COMMON_FIELDS(BCORE2),
+ .suspend = BFIELD(DA9063_REG_DVC_1, DA9063_VBCORE2_SEL),
+ .ilimit = BFIELD(DA9063_REG_BUCK_ILIM_C,
+ DA9063_BCORE2_ILIM_MASK),
+ },
+ {
+ DA9063_BUCK(DA9063, BPRO, 530, 10, 1800,
+ da9063_buck_a_limits),
+ DA9063_BUCK_COMMON_FIELDS(BPRO),
+ .suspend = BFIELD(DA9063_REG_DVC_1, DA9063_VBPRO_SEL),
+ .ilimit = BFIELD(DA9063_REG_BUCK_ILIM_B,
+ DA9063_BPRO_ILIM_MASK),
+ },
+ {
+ DA9063_BUCK(DA9063, BMEM, 800, 20, 3340,
+ da9063_buck_b_limits),
+ DA9063_BUCK_COMMON_FIELDS(BMEM),
+ .suspend = BFIELD(DA9063_REG_DVC_1, DA9063_VBMEM_SEL),
+ .ilimit = BFIELD(DA9063_REG_BUCK_ILIM_A,
+ DA9063_BMEM_ILIM_MASK),
+ },
+ {
+ DA9063_BUCK(DA9063, BIO, 800, 20, 3340,
+ da9063_buck_b_limits),
+ DA9063_BUCK_COMMON_FIELDS(BIO),
+ .suspend = BFIELD(DA9063_REG_DVC_2, DA9063_VBIO_SEL),
+ .ilimit = BFIELD(DA9063_REG_BUCK_ILIM_A,
+ DA9063_BIO_ILIM_MASK),
+ },
+ {
+ DA9063_BUCK(DA9063, BPERI, 800, 20, 3340,
+ da9063_buck_b_limits),
+ DA9063_BUCK_COMMON_FIELDS(BPERI),
+ .suspend = BFIELD(DA9063_REG_DVC_1, DA9063_VBPERI_SEL),
+ .ilimit = BFIELD(DA9063_REG_BUCK_ILIM_B,
+ DA9063_BPERI_ILIM_MASK),
+ },
+ {
+ DA9063_BUCK(DA9063, BCORES_MERGED, 300, 10, 1570,
+ da9063_bcores_merged_limits),
+ /* BCORES_MERGED uses the same register fields as BCORE1 */
+ DA9063_BUCK_COMMON_FIELDS(BCORE1),
+ .suspend = BFIELD(DA9063_REG_DVC_1, DA9063_VBCORE1_SEL),
+ .ilimit = BFIELD(DA9063_REG_BUCK_ILIM_C,
+ DA9063_BCORE1_ILIM_MASK),
+ },
+ {
+ DA9063_BUCK(DA9063, BMEM_BIO_MERGED, 800, 20, 3340,
+ da9063_bmem_bio_merged_limits),
+ /* BMEM_BIO_MERGED uses the same register fields as BMEM */
+ DA9063_BUCK_COMMON_FIELDS(BMEM),
+ .suspend = BFIELD(DA9063_REG_DVC_1, DA9063_VBMEM_SEL),
+ .ilimit = BFIELD(DA9063_REG_BUCK_ILIM_A,
+ DA9063_BMEM_ILIM_MASK),
+ },
+ {
+ DA9063_LDO(DA9063, LDO1, 600, 20, 1860),
+ .suspend = BFIELD(DA9063_REG_DVC_1, DA9063_VLDO1_SEL),
+ },
+ {
+ DA9063_LDO(DA9063, LDO2, 600, 20, 1860),
+ .suspend = BFIELD(DA9063_REG_DVC_1, DA9063_VLDO2_SEL),
+ },
+ {
+ DA9063_LDO(DA9063, LDO3, 900, 20, 3440),
+ .suspend = BFIELD(DA9063_REG_DVC_1, DA9063_VLDO3_SEL),
+ .oc_event = BFIELD(DA9063_REG_STATUS_D, DA9063_LDO3_LIM),
+ },
+ {
+ DA9063_LDO(DA9063, LDO4, 900, 20, 3440),
+ .suspend = BFIELD(DA9063_REG_DVC_2, DA9063_VLDO4_SEL),
+ .oc_event = BFIELD(DA9063_REG_STATUS_D, DA9063_LDO4_LIM),
+ },
+ {
+ DA9063_LDO(DA9063, LDO5, 900, 50, 3600),
+ .suspend = BFIELD(DA9063_REG_LDO5_CONT, DA9063_VLDO5_SEL),
+ },
+ {
+ DA9063_LDO(DA9063, LDO6, 900, 50, 3600),
+ .suspend = BFIELD(DA9063_REG_LDO6_CONT, DA9063_VLDO6_SEL),
+ },
+ {
+ DA9063_LDO(DA9063, LDO7, 900, 50, 3600),
+ .suspend = BFIELD(DA9063_REG_LDO7_CONT, DA9063_VLDO7_SEL),
+ .oc_event = BFIELD(DA9063_REG_STATUS_D, DA9063_LDO7_LIM),
+ },
+ {
+ DA9063_LDO(DA9063, LDO8, 900, 50, 3600),
+ .suspend = BFIELD(DA9063_REG_LDO8_CONT, DA9063_VLDO8_SEL),
+ .oc_event = BFIELD(DA9063_REG_STATUS_D, DA9063_LDO8_LIM),
+ },
+ {
+ DA9063_LDO(DA9063, LDO9, 950, 50, 3600),
+ .suspend = BFIELD(DA9063_REG_LDO9_CONT, DA9063_VLDO9_SEL),
+ },
+ {
+ DA9063_LDO(DA9063, LDO10, 900, 50, 3600),
+ .suspend = BFIELD(DA9063_REG_LDO10_CONT, DA9063_VLDO10_SEL),
+ },
+ {
+ DA9063_LDO(DA9063, LDO11, 900, 50, 3600),
+ .suspend = BFIELD(DA9063_REG_LDO11_CONT, DA9063_VLDO11_SEL),
+ .oc_event = BFIELD(DA9063_REG_STATUS_D, DA9063_LDO11_LIM),
+ },
+};
+
+/* Link chip model with regulators info table */
+static struct da9063_dev_model regulators_models[] = {
+ {
+ .regulator_info = da9063_regulator_info,
+ .n_regulators = ARRAY_SIZE(da9063_regulator_info),
+ .dev_model = PMIC_DA9063,
+ },
+ { }
+};
+
+/* Regulator interrupt handlers */
+static irqreturn_t da9063_ldo_lim_event(int irq, void *data)
+{
+ struct da9063_regulators *regulators = data;
+ struct da9063 *hw = regulators->regulator[0].hw;
+ struct da9063_regulator *regl;
+ int bits, i , ret;
+
+ ret = regmap_read(hw->regmap, DA9063_REG_STATUS_D, &bits);
+ if (ret < 0)
+ return IRQ_NONE;
+
+ for (i = regulators->n_regulators - 1; i >= 0; i--) {
+ regl = ®ulators->regulator[i];
+ if (regl->info->oc_event.reg != DA9063_REG_STATUS_D)
+ continue;
+
+ if (BIT(regl->info->oc_event.lsb) & bits)
+ regulator_notifier_call_chain(regl->rdev,
+ REGULATOR_EVENT_OVER_CURRENT, NULL);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Probing and Initialisation functions
+ */
+static const struct regulator_init_data *da9063_get_regulator_initdata(
+ const struct da9063_regulators_pdata *regl_pdata, int id)
+{
+ int i;
+
+ for (i = 0; i < regl_pdata->n_regulators; i++) {
+ if (id == regl_pdata->regulator_data[i].id)
+ return regl_pdata->regulator_data[i].initdata;
+ }
+
+ return NULL;
+}
+
+#ifdef CONFIG_OF
+static struct of_regulator_match da9063_matches[] = {
+ [DA9063_ID_BCORE1] = { .name = "bcore1" },
+ [DA9063_ID_BCORE2] = { .name = "bcore2" },
+ [DA9063_ID_BPRO] = { .name = "bpro", },
+ [DA9063_ID_BMEM] = { .name = "bmem", },
+ [DA9063_ID_BIO] = { .name = "bio", },
+ [DA9063_ID_BPERI] = { .name = "bperi", },
+ [DA9063_ID_BCORES_MERGED] = { .name = "bcores-merged" },
+ [DA9063_ID_BMEM_BIO_MERGED] = { .name = "bmem-bio-merged", },
+ [DA9063_ID_LDO1] = { .name = "ldo1", },
+ [DA9063_ID_LDO2] = { .name = "ldo2", },
+ [DA9063_ID_LDO3] = { .name = "ldo3", },
+ [DA9063_ID_LDO4] = { .name = "ldo4", },
+ [DA9063_ID_LDO5] = { .name = "ldo5", },
+ [DA9063_ID_LDO6] = { .name = "ldo6", },
+ [DA9063_ID_LDO7] = { .name = "ldo7", },
+ [DA9063_ID_LDO8] = { .name = "ldo8", },
+ [DA9063_ID_LDO9] = { .name = "ldo9", },
+ [DA9063_ID_LDO10] = { .name = "ldo10", },
+ [DA9063_ID_LDO11] = { .name = "ldo11", },
+};
+
+static struct da9063_regulators_pdata *da9063_parse_regulators_dt(
+ struct platform_device *pdev,
+ struct of_regulator_match **da9063_reg_matches)
+{
+ struct da9063_regulators_pdata *pdata;
+ struct da9063_regulator_data *rdata;
+ struct device_node *node;
+ int i, n, num;
+
+ node = of_find_node_by_name(pdev->dev.parent->of_node, "regulators");
+ if (!node) {
+ dev_err(&pdev->dev, "Regulators device node not found\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ num = of_regulator_match(&pdev->dev, node, da9063_matches,
+ ARRAY_SIZE(da9063_matches));
+ if (num < 0) {
+ dev_err(&pdev->dev, "Failed to match regulators\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ pdata->regulator_data = devm_kzalloc(&pdev->dev,
+ num * sizeof(*pdata->regulator_data),
+ GFP_KERNEL);
+ if (!pdata->regulator_data)
+ return ERR_PTR(-ENOMEM);
+ pdata->n_regulators = num;
+
+ n = 0;
+ for (i = 0; i < ARRAY_SIZE(da9063_matches); i++) {
+ if (!da9063_matches[i].init_data)
+ continue;
+
+ rdata = &pdata->regulator_data[n];
+ rdata->id = i;
+ rdata->initdata = da9063_matches[i].init_data;
+
+ n++;
+ };
+
+ *da9063_reg_matches = da9063_matches;
+ return pdata;
+}
+#else
+static struct da9063_regulators_pdata *da9063_parse_regulators_dt(
+ struct platform_device *pdev,
+ struct of_regulator_match **da9063_reg_matches)
+{
+ da9063_reg_matches = NULL;
+ return PTR_ERR(-ENODEV);
+}
+#endif
+
+static int da9063_regulator_probe(struct platform_device *pdev)
+{
+ struct da9063 *da9063 = dev_get_drvdata(pdev->dev.parent);
+ struct da9063_pdata *da9063_pdata = dev_get_platdata(da9063->dev);
+ struct of_regulator_match *da9063_reg_matches;
+ struct da9063_regulators_pdata *regl_pdata;
+ const struct da9063_dev_model *model;
+ struct da9063_regulators *regulators;
+ struct da9063_regulator *regl;
+ struct regulator_config config;
+ bool bcores_merged, bmem_bio_merged;
+ int id, irq, n, n_regulators, ret, val;
+ size_t size;
+
+ regl_pdata = da9063_pdata ? da9063_pdata->regulators_pdata : NULL;
+
+ if (!regl_pdata)
+ regl_pdata = da9063_parse_regulators_dt(pdev,
+ &da9063_reg_matches);
+
+ if (IS_ERR(regl_pdata) || regl_pdata->n_regulators == 0) {
+ dev_err(&pdev->dev,
+ "No regulators defined for the platform\n");
+ return PTR_ERR(regl_pdata);
+ }
+
+ /* Find regulators set for particular device model */
+ for (model = regulators_models; model->regulator_info; model++) {
+ if (model->dev_model == da9063->model)
+ break;
+ }
+ if (!model->regulator_info) {
+ dev_err(&pdev->dev, "Chip model not recognised (%u)\n",
+ da9063->model);
+ return -ENODEV;
+ }
+
+ ret = regmap_read(da9063->regmap, DA9063_REG_CONFIG_H, &val);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "Error while reading BUCKs configuration\n");
+ return -EIO;
+ }
+ bcores_merged = val & DA9063_BCORE_MERGE;
+ bmem_bio_merged = val & DA9063_BUCK_MERGE;
+
+ n_regulators = model->n_regulators;
+ if (bcores_merged)
+ n_regulators -= 2; /* remove BCORE1, BCORE2 */
+ else
+ n_regulators--; /* remove BCORES_MERGED */
+ if (bmem_bio_merged)
+ n_regulators -= 2; /* remove BMEM, BIO */
+ else
+ n_regulators--; /* remove BMEM_BIO_MERGED */
+
+ /* Allocate memory required by usable regulators */
+ size = sizeof(struct da9063_regulators) +
+ n_regulators * sizeof(struct da9063_regulator);
+ regulators = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
+ if (!regulators) {
+ dev_err(&pdev->dev, "No memory for regulators\n");
+ return -ENOMEM;
+ }
+
+ regulators->n_regulators = n_regulators;
+ platform_set_drvdata(pdev, regulators);
+
+ /* Register all regulators declared in platform information */
+ n = 0;
+ id = 0;
+ while (n < regulators->n_regulators) {
+ /* Skip regulator IDs depending on merge mode configuration */
+ switch (id) {
+ case DA9063_ID_BCORE1:
+ case DA9063_ID_BCORE2:
+ if (bcores_merged) {
+ id++;
+ continue;
+ }
+ break;
+ case DA9063_ID_BMEM:
+ case DA9063_ID_BIO:
+ if (bmem_bio_merged) {
+ id++;
+ continue;
+ }
+ break;
+ case DA9063_ID_BCORES_MERGED:
+ if (!bcores_merged) {
+ id++;
+ continue;
+ }
+ break;
+ case DA9063_ID_BMEM_BIO_MERGED:
+ if (!bmem_bio_merged) {
+ id++;
+ continue;
+ }
+ break;
+ }
+
+ /* Initialise regulator structure */
+ regl = ®ulators->regulator[n];
+ regl->hw = da9063;
+ regl->info = &model->regulator_info[id];
+ regl->desc = regl->info->desc;
+ regl->desc.type = REGULATOR_VOLTAGE;
+ regl->desc.owner = THIS_MODULE;
+
+ if (regl->info->mode.reg)
+ regl->mode = devm_regmap_field_alloc(&pdev->dev,
+ da9063->regmap, regl->info->mode);
+ if (regl->info->suspend.reg)
+ regl->suspend = devm_regmap_field_alloc(&pdev->dev,
+ da9063->regmap, regl->info->suspend);
+ if (regl->info->sleep.reg)
+ regl->sleep = devm_regmap_field_alloc(&pdev->dev,
+ da9063->regmap, regl->info->sleep);
+ if (regl->info->suspend_sleep.reg)
+ regl->suspend_sleep = devm_regmap_field_alloc(&pdev->dev,
+ da9063->regmap, regl->info->suspend_sleep);
+ if (regl->info->ilimit.reg)
+ regl->ilimit = devm_regmap_field_alloc(&pdev->dev,
+ da9063->regmap, regl->info->ilimit);
+
+ /* Register regulator */
+ memset(&config, 0, sizeof(config));
+ config.dev = &pdev->dev;
+ config.init_data = da9063_get_regulator_initdata(regl_pdata, id);
+ config.driver_data = regl;
+ if (da9063_reg_matches)
+ config.of_node = da9063_reg_matches[id].of_node;
+ config.regmap = da9063->regmap;
+ regl->rdev = regulator_register(®l->desc, &config);
+ if (IS_ERR(regl->rdev)) {
+ dev_err(&pdev->dev,
+ "Failed to register %s regulator\n",
+ regl->desc.name);
+ ret = PTR_ERR(regl->rdev);
+ goto err;
+ }
+ id++;
+ n++;
+ }
+
+ /* LDOs overcurrent event support */
+ irq = platform_get_irq_byname(pdev, "LDO_LIM");
+ if (irq < 0) {
+ ret = irq;
+ dev_err(&pdev->dev, "Failed to get IRQ.\n");
+ goto err;
+ }
+
+ 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;
+ }
+ }
+
+ return 0;
+
+err:
+ /* Wind back regulators registeration */
+ while (--n >= 0)
+ regulator_unregister(regulators->regulator[n].rdev);
+
+ return ret;
+}
+
+static int da9063_regulator_remove(struct platform_device *pdev)
+{
+ struct da9063_regulators *regulators = platform_get_drvdata(pdev);
+ struct da9063_regulator *regl;
+
+ free_irq(regulators->irq_ldo_lim, regulators);
+ free_irq(regulators->irq_uvov, regulators);
+
+ for (regl = ®ulators->regulator[regulators->n_regulators - 1];
+ regl >= ®ulators->regulator[0]; regl--)
+ regulator_unregister(regl->rdev);
+
+ return 0;
+}
+
+static struct platform_driver da9063_regulator_driver = {
+ .driver = {
+ .name = DA9063_DRVNAME_REGULATORS,
+ .owner = THIS_MODULE,
+ },
+ .probe = da9063_regulator_probe,
+ .remove = da9063_regulator_remove,
+};
+
+static int __init da9063_regulator_init(void)
+{
+ return platform_driver_register(&da9063_regulator_driver);
+}
+subsys_initcall(da9063_regulator_init);
+
+static void __exit da9063_regulator_cleanup(void)
+{
+ platform_driver_unregister(&da9063_regulator_driver);
+}
+module_exit(da9063_regulator_cleanup);
+
+
+/* Module information */
+MODULE_AUTHOR("Krystian Garbaciak <krystian.garbaciak@diasemi.com>");
+MODULE_DESCRIPTION("DA9063 regulators driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("paltform:" DA9063_DRVNAME_REGULATORS);
--- /dev/null
+/*
+ * da9210-regulator.c - Regulator device driver for DA9210
+ * Copyright (C) 2013 Dialog Semiconductor Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regmap.h>
+
+#include "da9210-regulator.h"
+
+struct da9210 {
+ struct regulator_dev *rdev;
+ struct regmap *regmap;
+};
+
+static const struct regmap_config da9210_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int da9210_set_current_limit(struct regulator_dev *rdev, int min_uA,
+ int max_uA);
+static int da9210_get_current_limit(struct regulator_dev *rdev);
+
+static struct regulator_ops da9210_buck_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_current_limit = da9210_set_current_limit,
+ .get_current_limit = da9210_get_current_limit,
+};
+
+/* Default limits measured in millivolts and milliamps */
+#define DA9210_MIN_MV 300
+#define DA9210_MAX_MV 1570
+#define DA9210_STEP_MV 10
+
+/* Current limits for buck (uA) indices corresponds with register values */
+static const int da9210_buck_limits[] = {
+ 1600000, 1800000, 2000000, 2200000, 2400000, 2600000, 2800000, 3000000,
+ 3200000, 3400000, 3600000, 3800000, 4000000, 4200000, 4400000, 4600000
+};
+
+static const struct regulator_desc da9210_reg = {
+ .name = "DA9210",
+ .id = 0,
+ .ops = &da9210_buck_ops,
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = ((DA9210_MAX_MV - DA9210_MIN_MV) / DA9210_STEP_MV) + 1,
+ .min_uV = (DA9210_MIN_MV * 1000),
+ .uV_step = (DA9210_STEP_MV * 1000),
+ .vsel_reg = DA9210_REG_VBUCK_A,
+ .vsel_mask = DA9210_VBUCK_MASK,
+ .enable_reg = DA9210_REG_BUCK_CONT,
+ .enable_mask = DA9210_BUCK_EN,
+ .owner = THIS_MODULE,
+};
+
+static int da9210_set_current_limit(struct regulator_dev *rdev, int min_uA,
+ int max_uA)
+{
+ struct da9210 *chip = rdev_get_drvdata(rdev);
+ unsigned int sel;
+ int i;
+
+ /* search for closest to maximum */
+ for (i = ARRAY_SIZE(da9210_buck_limits)-1; i >= 0; i--) {
+ if (min_uA <= da9210_buck_limits[i] &&
+ max_uA >= da9210_buck_limits[i]) {
+ sel = i;
+ sel = sel << DA9210_BUCK_ILIM_SHIFT;
+ return regmap_update_bits(chip->regmap,
+ DA9210_REG_BUCK_ILIM,
+ DA9210_BUCK_ILIM_MASK, sel);
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int da9210_get_current_limit(struct regulator_dev *rdev)
+{
+ struct da9210 *chip = rdev_get_drvdata(rdev);
+ unsigned int data;
+ unsigned int sel;
+ int ret;
+
+ ret = regmap_read(chip->regmap, DA9210_REG_BUCK_ILIM, &data);
+ if (ret < 0)
+ return ret;
+
+ /* select one of 16 values: 0000 (1600mA) to 1111 (4600mA) */
+ sel = (data & DA9210_BUCK_ILIM_MASK) >> DA9210_BUCK_ILIM_SHIFT;
+
+ return da9210_buck_limits[sel];
+}
+
+/*
+ * I2C driver interface functions
+ */
+static int da9210_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct da9210 *chip;
+ struct da9210_pdata *pdata = i2c->dev.platform_data;
+ struct regulator_dev *rdev = NULL;
+ struct regulator_config config = { };
+ int error;
+
+ chip = devm_kzalloc(&i2c->dev, sizeof(struct da9210), GFP_KERNEL);
+ if (NULL == chip) {
+ dev_err(&i2c->dev,
+ "Cannot kzalloc memory for regulator structure\n");
+ return -ENOMEM;
+ }
+
+ chip->regmap = devm_regmap_init_i2c(i2c, &da9210_regmap_config);
+ if (IS_ERR(chip->regmap)) {
+ error = PTR_ERR(chip->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ error);
+ return error;
+ }
+
+ config.dev = &i2c->dev;
+ if (pdata)
+ config.init_data = &pdata->da9210_constraints;
+ config.driver_data = chip;
+ config.regmap = chip->regmap;
+
+ rdev = regulator_register(&da9210_reg, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(&i2c->dev, "Failed to register DA9210 regulator\n");
+ return PTR_ERR(rdev);
+ }
+
+ chip->rdev = rdev;
+
+ i2c_set_clientdata(i2c, chip);
+
+ return 0;
+}
+
+static int da9210_i2c_remove(struct i2c_client *i2c)
+{
+ struct da9210 *chip = i2c_get_clientdata(i2c);
+ regulator_unregister(chip->rdev);
+ return 0;
+}
+
+static const struct i2c_device_id da9210_i2c_id[] = {
+ {"da9210", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, da9210_i2c_id);
+
+static struct i2c_driver da9210_regulator_driver = {
+ .driver = {
+ .name = "da9210",
+ .owner = THIS_MODULE,
+ },
+ .probe = da9210_i2c_probe,
+ .remove = da9210_i2c_remove,
+ .id_table = da9210_i2c_id,
+};
+
+module_i2c_driver(da9210_regulator_driver);
+
+MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>");
+MODULE_DESCRIPTION("Regulator device driver for Dialog DA9210");
+MODULE_LICENSE("GPL v2");
--- /dev/null
+
+/*
+ * da9210-regulator.h - Regulator definitions for DA9210
+ * Copyright (C) 2013 Dialog Semiconductor Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __DA9210_REGISTERS_H__
+#define __DA9210_REGISTERS_H__
+
+struct da9210_pdata {
+ struct regulator_init_data da9210_constraints;
+};
+
+/* Page selection */
+#define DA9210_REG_PAGE_CON 0x00
+
+/* System Control and Event Registers */
+#define DA9210_REG_STATUS_A 0x50
+#define DA9210_REG_STATUS_B 0x51
+#define DA9210_REG_EVENT_A 0x52
+#define DA9210_REG_EVENT_B 0x53
+#define DA9210_REG_MASK_A 0x54
+#define DA9210_REG_MASK_B 0x55
+#define DA9210_REG_CONTROL_A 0x56
+
+/* GPIO Control Registers */
+#define DA9210_REG_GPIO_0_1 0x58
+#define DA9210_REG_GPIO_2_3 0x59
+#define DA9210_REG_GPIO_4_5 0x5A
+#define DA9210_REG_GPIO_6 0x5B
+
+/* Regulator Registers */
+#define DA9210_REG_BUCK_CONT 0x5D
+#define DA9210_REG_BUCK_ILIM 0xD0
+#define DA9210_REG_BUCK_CONF1 0xD1
+#define DA9210_REG_BUCK_CONF2 0xD2
+#define DA9210_REG_VBACK_AUTO 0xD4
+#define DA9210_REG_VBACK_BASE 0xD5
+#define DA9210_REG_VBACK_MAX_DVC_IF 0xD6
+#define DA9210_REG_VBACK_DVC 0xD7
+#define DA9210_REG_VBUCK_A 0xD8
+#define DA9210_REG_VBUCK_B 0xD9
+
+/* I2C Interface Settings */
+#define DA9210_REG_INTERFACE 0x105
+
+/* OTP */
+#define DA9210_REG_OPT_COUNT 0x140
+#define DA9210_REG_OPT_ADDR 0x141
+#define DA9210_REG_OPT_DATA 0x142
+
+/* Customer Trim and Configuration */
+#define DA9210_REG_CONFIG_A 0x143
+#define DA9210_REG_CONFIG_B 0x144
+#define DA9210_REG_CONFIG_C 0x145
+#define DA9210_REG_CONFIG_D 0x146
+#define DA9210_REG_CONFIG_E 0x147
+
+
+/*
+ * Registers bits
+ */
+/* DA9210_REG_PAGE_CON (addr=0x00) */
+#define DA9210_PEG_PAGE_SHIFT 0
+#define DA9210_REG_PAGE_MASK 0x0F
+/* On I2C registers 0x00 - 0xFF */
+#define DA9210_REG_PAGE0 0
+/* On I2C registers 0x100 - 0x1FF */
+#define DA9210_REG_PAGE2 2
+#define DA9210_PAGE_WRITE_MODE 0x00
+#define DA9210_REPEAT_WRITE_MODE 0x40
+#define DA9210_PAGE_REVERT 0x80
+
+/* DA9210_REG_STATUS_A (addr=0x50) */
+#define DA9210_GPI0 0x01
+#define DA9210_GPI1 0x02
+#define DA9210_GPI2 0x04
+#define DA9210_GPI3 0x08
+#define DA9210_GPI4 0x10
+#define DA9210_GPI5 0x20
+#define DA9210_GPI6 0x40
+
+/* DA9210_REG_EVENT_A (addr=0x52) */
+#define DA9210_E_GPI0 0x01
+#define DA9210_E_GPI1 0x02
+#define DA9210_E_GPI2 0x04
+#define DA9210_E_GPI3 0x08
+#define DA9210_E_GPI4 0x10
+#define DA9210_E_GPI5 0x20
+#define DA9210_E_GPI6 0x40
+
+/* DA9210_REG_EVENT_B (addr=0x53) */
+#define DA9210_E_OVCURR 0x01
+#define DA9210_E_NPWRGOOD 0x02
+#define DA9210_E_TEMP_WARN 0x04
+#define DA9210_E_TEMP_CRIT 0x08
+#define DA9210_E_VMAX 0x10
+
+/* DA9210_REG_MASK_A (addr=0x54) */
+#define DA9210_M_GPI0 0x01
+#define DA9210_M_GPI1 0x02
+#define DA9210_M_GPI2 0x04
+#define DA9210_M_GPI3 0x08
+#define DA9210_M_GPI4 0x10
+#define DA9210_M_GPI5 0x20
+#define DA9210_M_GPI6 0x40
+
+/* DA9210_REG_MASK_B (addr=0x55) */
+#define DA9210_M_OVCURR 0x01
+#define DA9210_M_NPWRGOOD 0x02
+#define DA9210_M_TEMP_WARN 0x04
+#define DA9210_M_TEMP_CRIT 0x08
+#define DA9210_M_VMAX 0x10
+
+/* DA9210_REG_CONTROL_A (addr=0x56) */
+#define DA9210_DEBOUNCING_SHIFT 0
+#define DA9210_DEBOUNCING_MASK 0x07
+#define DA9210_SLEW_RATE_SHIFT 3
+#define DA9210_SLEW_RATE_MASK 0x18
+#define DA9210_V_LOCK 0x20
+
+/* DA9210_REG_GPIO_0_1 (addr=0x58) */
+#define DA9210_GPIO0_PIN_SHIFT 0
+#define DA9210_GPIO0_PIN_MASK 0x03
+#define DA9210_GPIO0_PIN_GPI 0x00
+#define DA9210_GPIO0_PIN_GPO_OD 0x02
+#define DA9210_GPIO0_PIN_GPO 0x03
+#define DA9210_GPIO0_TYPE 0x04
+#define DA9210_GPIO0_TYPE_GPI 0x00
+#define DA9210_GPIO0_TYPE_GPO 0x04
+#define DA9210_GPIO0_MODE 0x08
+#define DA9210_GPIO1_PIN_SHIFT 4
+#define DA9210_GPIO1_PIN_MASK 0x30
+#define DA9210_GPIO1_PIN_GPI 0x00
+#define DA9210_GPIO1_PIN_VERROR 0x10
+#define DA9210_GPIO1_PIN_GPO_OD 0x20
+#define DA9210_GPIO1_PIN_GPO 0x30
+#define DA9210_GPIO1_TYPE_SHIFT 0x40
+#define DA9210_GPIO1_TYPE_GPI 0x00
+#define DA9210_GPIO1_TYPE_GPO 0x40
+#define DA9210_GPIO1_MODE 0x80
+
+/* DA9210_REG_GPIO_2_3 (addr=0x59) */
+#define DA9210_GPIO2_PIN_SHIFT 0
+#define DA9210_GPIO2_PIN_MASK 0x03
+#define DA9210_GPIO2_PIN_GPI 0x00
+#define DA9210_GPIO5_PIN_BUCK_CLK 0x10
+#define DA9210_GPIO2_PIN_GPO_OD 0x02
+#define DA9210_GPIO2_PIN_GPO 0x03
+#define DA9210_GPIO2_TYPE 0x04
+#define DA9210_GPIO2_TYPE_GPI 0x00
+#define DA9210_GPIO2_TYPE_GPO 0x04
+#define DA9210_GPIO2_MODE 0x08
+#define DA9210_GPIO3_PIN_SHIFT 4
+#define DA9210_GPIO3_PIN_MASK 0x30
+#define DA9210_GPIO3_PIN_GPI 0x00
+#define DA9210_GPIO3_PIN_IERROR 0x10
+#define DA9210_GPIO3_PIN_GPO_OD 0x20
+#define DA9210_GPIO3_PIN_GPO 0x30
+#define DA9210_GPIO3_TYPE_SHIFT 0x40
+#define DA9210_GPIO3_TYPE_GPI 0x00
+#define DA9210_GPIO3_TYPE_GPO 0x40
+#define DA9210_GPIO3_MODE 0x80
+
+/* DA9210_REG_GPIO_4_5 (addr=0x5A) */
+#define DA9210_GPIO4_PIN_SHIFT 0
+#define DA9210_GPIO4_PIN_MASK 0x03
+#define DA9210_GPIO4_PIN_GPI 0x00
+#define DA9210_GPIO4_PIN_GPO_OD 0x02
+#define DA9210_GPIO4_PIN_GPO 0x03
+#define DA9210_GPIO4_TYPE 0x04
+#define DA9210_GPIO4_TYPE_GPI 0x00
+#define DA9210_GPIO4_TYPE_GPO 0x04
+#define DA9210_GPIO4_MODE 0x08
+#define DA9210_GPIO5_PIN_SHIFT 4
+#define DA9210_GPIO5_PIN_MASK 0x30
+#define DA9210_GPIO5_PIN_GPI 0x00
+#define DA9210_GPIO5_PIN_INTERFACE 0x01
+#define DA9210_GPIO5_PIN_GPO_OD 0x20
+#define DA9210_GPIO5_PIN_GPO 0x30
+#define DA9210_GPIO5_TYPE_SHIFT 0x40
+#define DA9210_GPIO5_TYPE_GPI 0x00
+#define DA9210_GPIO5_TYPE_GPO 0x40
+#define DA9210_GPIO5_MODE 0x80
+
+/* DA9210_REG_GPIO_6 (addr=0x5B) */
+#define DA9210_GPIO6_PIN_SHIFT 0
+#define DA9210_GPIO6_PIN_MASK 0x03
+#define DA9210_GPIO6_PIN_GPI 0x00
+#define DA9210_GPIO6_PIN_INTERFACE 0x01
+#define DA9210_GPIO6_PIN_GPO_OD 0x02
+#define DA9210_GPIO6_PIN_GPO 0x03
+#define DA9210_GPIO6_TYPE 0x04
+#define DA9210_GPIO6_TYPE_GPI 0x00
+#define DA9210_GPIO6_TYPE_GPO 0x04
+#define DA9210_GPIO6_MODE 0x08
+
+/* DA9210_REG_BUCK_CONT (addr=0x5D) */
+#define DA9210_BUCK_EN 0x01
+#define DA9210_BUCK_GPI_SHIFT 1
+#define DA9210_BUCK_GPI_MASK 0x06
+#define DA9210_BUCK_GPI_OFF 0x00
+#define DA9210_BUCK_GPI_GPIO0 0x02
+#define DA9210_BUCK_GPI_GPIO3 0x04
+#define DA9210_BUCK_GPI_GPIO4 0x06
+#define DA9210_BUCK_PD_DIS 0x08
+#define DA9210_VBUCK_SEL 0x10
+#define DA9210_VBUCK_SEL_A 0x00
+#define DA9210_VBUCK_SEL_B 0x10
+#define DA9210_VBUCK_GPI_SHIFT 5
+#define DA9210_VBUCK_GPI_MASK 0x60
+#define DA9210_VBUCK_GPI_OFF 0x00
+#define DA9210_VBUCK_GPI_GPIO0 0x20
+#define DA9210_VBUCK_GPI_GPIO3 0x40
+#define DA9210_VBUCK_GPI_GPIO4 0x60
+#define DA9210_DVC_CTRL_EN 0x80
+
+/* DA9210_REG_BUCK_ILIM (addr=0xD0) */
+#define DA9210_BUCK_ILIM_SHIFT 0
+#define DA9210_BUCK_ILIM_MASK 0x0F
+#define DA9210_BUCK_IALARM 0x10
+
+/* DA9210_REG_BUCK_CONF1 (addr=0xD1) */
+#define DA9210_BUCK_MODE_SHIFT 0
+#define DA9210_BUCK_MODE_MASK 0x03
+#define DA9210_BUCK_MODE_MANUAL 0x00
+#define DA9210_BUCK_MODE_SLEEP 0x01
+#define DA9210_BUCK_MODE_SYNC 0x02
+#define DA9210_BUCK_MODE_AUTO 0x03
+#define DA9210_STARTUP_CTRL_SHIFT 2
+#define DA9210_STARTUP_CTRL_MASK 0x1C
+#define DA9210_PWR_DOWN_CTRL_SHIFT 5
+#define DA9210_PWR_DOWN_CTRL_MASK 0xE0
+
+/* DA9210_REG_BUCK_CONF2 (addr=0xD2) */
+#define DA9210_PHASE_SEL_SHIFT 0
+#define DA9210_PHASE_SEL_MASK 0x03
+#define DA9210_FREQ_SEL 0x40
+
+/* DA9210_REG_BUCK_AUTO (addr=0xD4) */
+#define DA9210_VBUCK_AUTO_SHIFT 0
+#define DA9210_VBUCK_AUTO_MASK 0x7F
+
+/* DA9210_REG_BUCK_BASE (addr=0xD5) */
+#define DA9210_VBUCK_BASE_SHIFT 0
+#define DA9210_VBUCK_BASE_MASK 0x7F
+
+/* DA9210_REG_VBUCK_MAX_DVC_IF (addr=0xD6) */
+#define DA9210_VBUCK_MAX_SHIFT 0
+#define DA9210_VBUCK_MAX_MASK 0x7F
+#define DA9210_DVC_STEP_SIZE 0x80
+#define DA9210_DVC_STEP_SIZE_10MV 0x00
+#define DA9210_DVC_STEP_SIZE_20MV 0x80
+
+/* DA9210_REG_VBUCK_DVC (addr=0xD7) */
+#define DA9210_VBUCK_DVC_SHIFT 0
+#define DA9210_VBUCK_DVC_MASK 0x7F
+
+/* DA9210_REG_VBUCK_A/B (addr=0xD8/0xD9) */
+#define DA9210_VBUCK_SHIFT 0
+#define DA9210_VBUCK_MASK 0x7F
+#define DA9210_VBUCK_BIAS 0
+#define DA9210_BUCK_SL 0x80
+
+/* DA9210_REG_INTERFACE (addr=0x105) */
+#define DA9210_IF_BASE_ADDR_SHIFT 4
+#define DA9210_IF_BASE_ADDR_MASK 0xF0
+
+/* DA9210_REG_CONFIG_E (addr=0x147) */
+#define DA9210_STAND_ALONE 0x01
+
+#endif /* __DA9210_REGISTERS_H__ */
+
unsigned int val;
int ret;
- pdata = client->dev.platform_data;
+ pdata = dev_get_platdata(&client->dev);
if (!pdata || !pdata->regulator) {
dev_err(&client->dev, "Platform data not found!\n");
return -ENODEV;
if (IS_ERR(config))
return PTR_ERR(config);
} else {
- config = pdev->dev.platform_data;
+ config = dev_get_platdata(&pdev->dev);
}
if (!config)
static int gpio_regulator_probe(struct platform_device *pdev)
{
- struct gpio_regulator_config *config = pdev->dev.platform_data;
+ struct gpio_regulator_config *config = dev_get_platdata(&pdev->dev);
struct device_node *np = pdev->dev.of_node;
struct gpio_regulator_data *drvdata;
struct regulator_config cfg = { };
--- /dev/null
+/*
+ * helpers.c -- Voltage/Current Regulator framework helper functions.
+ *
+ * Copyright 2007, 2008 Wolfson Microelectronics PLC.
+ * Copyright 2008 SlimLogic 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/driver.h>
+#include <linux/module.h>
+
+/**
+ * regulator_is_enabled_regmap - standard is_enabled() for regmap users
+ *
+ * @rdev: regulator to operate on
+ *
+ * Regulators that use regmap for their register I/O can set the
+ * enable_reg and enable_mask fields in their descriptor and then use
+ * this as their is_enabled operation, saving some code.
+ */
+int regulator_is_enabled_regmap(struct regulator_dev *rdev)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(rdev->regmap, rdev->desc->enable_reg, &val);
+ if (ret != 0)
+ return ret;
+
+ if (rdev->desc->enable_is_inverted)
+ return (val & rdev->desc->enable_mask) == 0;
+ else
+ return (val & rdev->desc->enable_mask) != 0;
+}
+EXPORT_SYMBOL_GPL(regulator_is_enabled_regmap);
+
+/**
+ * regulator_enable_regmap - standard enable() for regmap users
+ *
+ * @rdev: regulator to operate on
+ *
+ * Regulators that use regmap for their register I/O can set the
+ * enable_reg and enable_mask fields in their descriptor and then use
+ * this as their enable() operation, saving some code.
+ */
+int regulator_enable_regmap(struct regulator_dev *rdev)
+{
+ unsigned int val;
+
+ if (rdev->desc->enable_is_inverted)
+ val = 0;
+ else
+ val = rdev->desc->enable_mask;
+
+ return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+ rdev->desc->enable_mask, val);
+}
+EXPORT_SYMBOL_GPL(regulator_enable_regmap);
+
+/**
+ * regulator_disable_regmap - standard disable() for regmap users
+ *
+ * @rdev: regulator to operate on
+ *
+ * Regulators that use regmap for their register I/O can set the
+ * enable_reg and enable_mask fields in their descriptor and then use
+ * this as their disable() operation, saving some code.
+ */
+int regulator_disable_regmap(struct regulator_dev *rdev)
+{
+ unsigned int val;
+
+ if (rdev->desc->enable_is_inverted)
+ val = rdev->desc->enable_mask;
+ else
+ val = 0;
+
+ return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+ rdev->desc->enable_mask, val);
+}
+EXPORT_SYMBOL_GPL(regulator_disable_regmap);
+
+/**
+ * regulator_get_voltage_sel_regmap - standard get_voltage_sel for regmap users
+ *
+ * @rdev: regulator to operate on
+ *
+ * Regulators that use regmap for their register I/O can set the
+ * vsel_reg and vsel_mask fields in their descriptor and then use this
+ * as their get_voltage_vsel operation, saving some code.
+ */
+int regulator_get_voltage_sel_regmap(struct regulator_dev *rdev)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(rdev->regmap, rdev->desc->vsel_reg, &val);
+ if (ret != 0)
+ return ret;
+
+ val &= rdev->desc->vsel_mask;
+ val >>= ffs(rdev->desc->vsel_mask) - 1;
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(regulator_get_voltage_sel_regmap);
+
+/**
+ * regulator_set_voltage_sel_regmap - standard set_voltage_sel for regmap users
+ *
+ * @rdev: regulator to operate on
+ * @sel: Selector to set
+ *
+ * Regulators that use regmap for their register I/O can set the
+ * vsel_reg and vsel_mask fields in their descriptor and then use this
+ * as their set_voltage_vsel operation, saving some code.
+ */
+int regulator_set_voltage_sel_regmap(struct regulator_dev *rdev, unsigned sel)
+{
+ int ret;
+
+ sel <<= ffs(rdev->desc->vsel_mask) - 1;
+
+ ret = regmap_update_bits(rdev->regmap, rdev->desc->vsel_reg,
+ rdev->desc->vsel_mask, sel);
+ if (ret)
+ return ret;
+
+ if (rdev->desc->apply_bit)
+ ret = regmap_update_bits(rdev->regmap, rdev->desc->apply_reg,
+ rdev->desc->apply_bit,
+ rdev->desc->apply_bit);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_set_voltage_sel_regmap);
+
+/**
+ * regulator_map_voltage_iterate - map_voltage() based on list_voltage()
+ *
+ * @rdev: Regulator to operate on
+ * @min_uV: Lower bound for voltage
+ * @max_uV: Upper bound for voltage
+ *
+ * Drivers implementing set_voltage_sel() and list_voltage() can use
+ * this as their map_voltage() operation. It will find a suitable
+ * voltage by calling list_voltage() until it gets something in bounds
+ * for the requested voltages.
+ */
+int regulator_map_voltage_iterate(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ int best_val = INT_MAX;
+ int selector = 0;
+ int i, ret;
+
+ /* Find the smallest voltage that falls within the specified
+ * range.
+ */
+ for (i = 0; i < rdev->desc->n_voltages; i++) {
+ ret = rdev->desc->ops->list_voltage(rdev, i);
+ if (ret < 0)
+ continue;
+
+ if (ret < best_val && ret >= min_uV && ret <= max_uV) {
+ best_val = ret;
+ selector = i;
+ }
+ }
+
+ if (best_val != INT_MAX)
+ return selector;
+ else
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(regulator_map_voltage_iterate);
+
+/**
+ * regulator_map_voltage_ascend - map_voltage() for ascendant voltage list
+ *
+ * @rdev: Regulator to operate on
+ * @min_uV: Lower bound for voltage
+ * @max_uV: Upper bound for voltage
+ *
+ * Drivers that have ascendant voltage list can use this as their
+ * map_voltage() operation.
+ */
+int regulator_map_voltage_ascend(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ int i, ret;
+
+ for (i = 0; i < rdev->desc->n_voltages; i++) {
+ ret = rdev->desc->ops->list_voltage(rdev, i);
+ if (ret < 0)
+ continue;
+
+ if (ret > max_uV)
+ break;
+
+ if (ret >= min_uV && ret <= max_uV)
+ return i;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(regulator_map_voltage_ascend);
+
+/**
+ * regulator_map_voltage_linear - map_voltage() for simple linear mappings
+ *
+ * @rdev: Regulator to operate on
+ * @min_uV: Lower bound for voltage
+ * @max_uV: Upper bound for voltage
+ *
+ * Drivers providing min_uV and uV_step in their regulator_desc can
+ * use this as their map_voltage() operation.
+ */
+int regulator_map_voltage_linear(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ int ret, voltage;
+
+ /* Allow uV_step to be 0 for fixed voltage */
+ if (rdev->desc->n_voltages == 1 && rdev->desc->uV_step == 0) {
+ if (min_uV <= rdev->desc->min_uV && rdev->desc->min_uV <= max_uV)
+ return 0;
+ else
+ return -EINVAL;
+ }
+
+ if (!rdev->desc->uV_step) {
+ BUG_ON(!rdev->desc->uV_step);
+ return -EINVAL;
+ }
+
+ if (min_uV < rdev->desc->min_uV)
+ min_uV = rdev->desc->min_uV;
+
+ ret = DIV_ROUND_UP(min_uV - rdev->desc->min_uV, rdev->desc->uV_step);
+ if (ret < 0)
+ return ret;
+
+ ret += rdev->desc->linear_min_sel;
+
+ /* Map back into a voltage to verify we're still in bounds */
+ voltage = rdev->desc->ops->list_voltage(rdev, ret);
+ if (voltage < min_uV || voltage > max_uV)
+ return -EINVAL;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_map_voltage_linear);
+
+/**
+ * regulator_map_voltage_linear - map_voltage() for multiple linear ranges
+ *
+ * @rdev: Regulator to operate on
+ * @min_uV: Lower bound for voltage
+ * @max_uV: Upper bound for voltage
+ *
+ * Drivers providing linear_ranges in their descriptor can use this as
+ * their map_voltage() callback.
+ */
+int regulator_map_voltage_linear_range(struct regulator_dev *rdev,
+ int min_uV, int max_uV)
+{
+ const struct regulator_linear_range *range;
+ int ret = -EINVAL;
+ int voltage, i;
+
+ if (!rdev->desc->n_linear_ranges) {
+ BUG_ON(!rdev->desc->n_linear_ranges);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < rdev->desc->n_linear_ranges; i++) {
+ range = &rdev->desc->linear_ranges[i];
+
+ if (!(min_uV <= range->max_uV && max_uV >= range->min_uV))
+ continue;
+
+ if (min_uV <= range->min_uV)
+ min_uV = range->min_uV;
+
+ /* range->uV_step == 0 means fixed voltage range */
+ if (range->uV_step == 0) {
+ ret = 0;
+ } else {
+ ret = DIV_ROUND_UP(min_uV - range->min_uV,
+ range->uV_step);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret += range->min_sel;
+
+ break;
+ }
+
+ if (i == rdev->desc->n_linear_ranges)
+ return -EINVAL;
+
+ /* Map back into a voltage to verify we're still in bounds */
+ voltage = rdev->desc->ops->list_voltage(rdev, ret);
+ if (voltage < min_uV || voltage > max_uV)
+ return -EINVAL;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_map_voltage_linear_range);
+
+/**
+ * regulator_list_voltage_linear - List voltages with simple calculation
+ *
+ * @rdev: Regulator device
+ * @selector: Selector to convert into a voltage
+ *
+ * Regulators with a simple linear mapping between voltages and
+ * selectors can set min_uV and uV_step in the regulator descriptor
+ * and then use this function as their list_voltage() operation,
+ */
+int regulator_list_voltage_linear(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ if (selector >= rdev->desc->n_voltages)
+ return -EINVAL;
+ if (selector < rdev->desc->linear_min_sel)
+ return 0;
+
+ selector -= rdev->desc->linear_min_sel;
+
+ return rdev->desc->min_uV + (rdev->desc->uV_step * selector);
+}
+EXPORT_SYMBOL_GPL(regulator_list_voltage_linear);
+
+/**
+ * regulator_list_voltage_linear_range - List voltages for linear ranges
+ *
+ * @rdev: Regulator device
+ * @selector: Selector to convert into a voltage
+ *
+ * Regulators with a series of simple linear mappings between voltages
+ * and selectors can set linear_ranges in the regulator descriptor and
+ * then use this function as their list_voltage() operation,
+ */
+int regulator_list_voltage_linear_range(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ const struct regulator_linear_range *range;
+ int i;
+
+ if (!rdev->desc->n_linear_ranges) {
+ BUG_ON(!rdev->desc->n_linear_ranges);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < rdev->desc->n_linear_ranges; i++) {
+ range = &rdev->desc->linear_ranges[i];
+
+ if (!(selector >= range->min_sel &&
+ selector <= range->max_sel))
+ continue;
+
+ selector -= range->min_sel;
+
+ return range->min_uV + (range->uV_step * selector);
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(regulator_list_voltage_linear_range);
+
+/**
+ * regulator_list_voltage_table - List voltages with table based mapping
+ *
+ * @rdev: Regulator device
+ * @selector: Selector to convert into a voltage
+ *
+ * Regulators with table based mapping between voltages and
+ * selectors can set volt_table in the regulator descriptor
+ * and then use this function as their list_voltage() operation.
+ */
+int regulator_list_voltage_table(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ if (!rdev->desc->volt_table) {
+ BUG_ON(!rdev->desc->volt_table);
+ return -EINVAL;
+ }
+
+ if (selector >= rdev->desc->n_voltages)
+ return -EINVAL;
+
+ return rdev->desc->volt_table[selector];
+}
+EXPORT_SYMBOL_GPL(regulator_list_voltage_table);
+
+/**
+ * regulator_set_bypass_regmap - Default set_bypass() using regmap
+ *
+ * @rdev: device to operate on.
+ * @enable: state to set.
+ */
+int regulator_set_bypass_regmap(struct regulator_dev *rdev, bool enable)
+{
+ unsigned int val;
+
+ if (enable)
+ val = rdev->desc->bypass_mask;
+ else
+ val = 0;
+
+ return regmap_update_bits(rdev->regmap, rdev->desc->bypass_reg,
+ rdev->desc->bypass_mask, val);
+}
+EXPORT_SYMBOL_GPL(regulator_set_bypass_regmap);
+
+/**
+ * regulator_get_bypass_regmap - Default get_bypass() using regmap
+ *
+ * @rdev: device to operate on.
+ * @enable: current state.
+ */
+int regulator_get_bypass_regmap(struct regulator_dev *rdev, bool *enable)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(rdev->regmap, rdev->desc->bypass_reg, &val);
+ if (ret != 0)
+ return ret;
+
+ *enable = val & rdev->desc->bypass_mask;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(regulator_get_bypass_regmap);
const struct i2c_device_id *id)
{
struct regulator_config config = { };
- struct regulator_init_data *init_data = i2c->dev.platform_data;
+ struct regulator_init_data *init_data = dev_get_platdata(&i2c->dev);
struct isl_pmic *pmic;
int err, i;
const struct i2c_device_id *id)
{
struct lp3971 *lp3971;
- struct lp3971_platform_data *pdata = i2c->dev.platform_data;
+ struct lp3971_platform_data *pdata = dev_get_platdata(&i2c->dev);
int ret;
u16 val;
const struct i2c_device_id *id)
{
struct lp3972 *lp3972;
- struct lp3972_platform_data *pdata = i2c->dev.platform_data;
+ struct lp3972_platform_data *pdata = dev_get_platdata(&i2c->dev);
int ret;
u16 val;
return -EINVAL;
}
- for (i = ARRAY_SIZE(lp8725_buck_uA) - 1 ; i >= 0; i--) {
+ for (i = ARRAY_SIZE(lp8725_buck_uA) - 1; i >= 0; i--) {
if (lp8725_buck_uA[i] >= min_uA &&
lp8725_buck_uA[i] <= max_uA)
return lp872x_update_bits(lp, addr,
struct regulator_dev *rdev;
int i, ret;
- for (i = 0 ; i < lp->num_regulators ; i++) {
+ for (i = 0; i < lp->num_regulators; i++) {
desc = (lp->chipid == LP8720) ? &lp8720_regulator_desc[i] :
&lp8725_regulator_desc[i];
struct regulator_dev *rdev;
int i;
- for (i = 0 ; i < lp->num_regulators ; i++) {
+ for (i = 0; i < lp->num_regulators; i++) {
rdev = *(lp->regulators + i);
regulator_unregister(rdev);
}
goto out;
for (i = 0; i < num_matches; i++) {
- pdata->regulator_data[i].id = (int)match[i].driver_data;
+ pdata->regulator_data[i].id =
+ (enum lp872x_regulator_id)match[i].driver_data;
pdata->regulator_data[i].init_data = match[i].init_data;
/* Operation mode configuration for buck/buck1/buck2 */
}
lp->dev = &cl->dev;
- lp->pdata = cl->dev.platform_data;
+ lp->pdata = dev_get_platdata(&cl->dev);
lp->chipid = id->driver_data;
lp->num_regulators = num_regulators;
i2c_set_clientdata(cl, lp);
}
static struct regulator_ops lp8755_buck_ops = {
+ .map_voltage = regulator_map_voltage_linear,
.list_voltage = regulator_list_voltage_linear,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
{
int ret, icnt;
struct lp8755_chip *pchip;
- struct lp8755_platform_data *pdata = client->dev.platform_data;
+ struct lp8755_platform_data *pdata = dev_get_platdata(&client->dev);
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(&client->dev, "i2c functionality check fail.\n");
const struct i2c_device_id *i2c_id)
{
struct regulator_dev **rdev;
- struct max1586_platform_data *pdata = client->dev.platform_data;
+ struct max1586_platform_data *pdata = dev_get_platdata(&client->dev);
struct regulator_config config = { };
struct max1586_data *max1586;
int i, id, ret = -ENOMEM;
static int max8649_regulator_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- struct max8649_platform_data *pdata = client->dev.platform_data;
+ struct max8649_platform_data *pdata = dev_get_platdata(&client->dev);
struct max8649_regulator_info *info = NULL;
struct regulator_config config = { };
unsigned int val;
#include <linux/regulator/driver.h>
#include <linux/slab.h>
#include <linux/regulator/max8660.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regulator/of_regulator.h>
#define MAX8660_DCDC_MIN_UV 725000
#define MAX8660_DCDC_MAX_UV 1800000
},
};
+enum {
+ MAX8660 = 0,
+ MAX8661 = 1,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id max8660_dt_ids[] = {
+ { .compatible = "maxim,max8660", .data = (void *) MAX8660 },
+ { .compatible = "maxim,max8661", .data = (void *) MAX8661 },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max8660_dt_ids);
+
+static int max8660_pdata_from_dt(struct device *dev,
+ struct device_node **of_node,
+ struct max8660_platform_data *pdata)
+{
+ int matched, i;
+ struct device_node *np;
+ struct max8660_subdev_data *sub;
+ struct of_regulator_match rmatch[ARRAY_SIZE(max8660_reg)];
+
+ np = of_find_node_by_name(dev->of_node, "regulators");
+ if (!np) {
+ dev_err(dev, "missing 'regulators' subnode in DT\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(rmatch); i++)
+ rmatch[i].name = max8660_reg[i].name;
+
+ matched = of_regulator_match(dev, np, rmatch, ARRAY_SIZE(rmatch));
+ if (matched <= 0)
+ return matched;
+
+ pdata->subdevs = devm_kzalloc(dev, sizeof(struct max8660_subdev_data) *
+ matched, GFP_KERNEL);
+ if (!pdata->subdevs)
+ return -ENOMEM;
+
+ pdata->num_subdevs = matched;
+ sub = pdata->subdevs;
+
+ for (i = 0; i < matched; i++) {
+ sub->id = i;
+ sub->name = rmatch[i].name;
+ sub->platform_data = rmatch[i].init_data;
+ of_node[i] = rmatch[i].of_node;
+ sub++;
+ }
+
+ return 0;
+}
+#else
+static inline int max8660_pdata_from_dt(struct device *dev,
+ struct device_node **of_node,
+ struct max8660_platform_data *pdata)
+{
+ return 0;
+}
+#endif
+
static int max8660_probe(struct i2c_client *client,
const struct i2c_device_id *i2c_id)
{
struct regulator_dev **rdev;
- struct max8660_platform_data *pdata = client->dev.platform_data;
+ struct device *dev = &client->dev;
+ struct max8660_platform_data *pdata = dev_get_platdata(dev);
struct regulator_config config = { };
struct max8660 *max8660;
int boot_on, i, id, ret = -EINVAL;
+ struct device_node *of_node[MAX8660_V_END];
+ unsigned long type;
+
+ if (dev->of_node && !pdata) {
+ const struct of_device_id *id;
+ struct max8660_platform_data pdata_of;
+
+ id = of_match_device(of_match_ptr(max8660_dt_ids), dev);
+ if (!id)
+ return -ENODEV;
+
+ ret = max8660_pdata_from_dt(dev, of_node, &pdata_of);
+ if (ret < 0)
+ return ret;
+
+ pdata = &pdata_of;
+ type = (unsigned long) id->data;
+ } else {
+ type = i2c_id->driver_data;
+ memset(of_node, 0, sizeof(of_node));
+ }
if (pdata->num_subdevs > MAX8660_V_END) {
- dev_err(&client->dev, "Too many regulators found!\n");
+ dev_err(dev, "Too many regulators found!\n");
return -EINVAL;
}
- max8660 = devm_kzalloc(&client->dev, sizeof(struct max8660) +
+ max8660 = devm_kzalloc(dev, sizeof(struct max8660) +
sizeof(struct regulator_dev *) * MAX8660_V_END,
GFP_KERNEL);
if (!max8660)
break;
case MAX8660_V7:
- if (!strcmp(i2c_id->name, "max8661")) {
- dev_err(&client->dev, "Regulator not on this chip!\n");
+ if (type == MAX8661) {
+ dev_err(dev, "Regulator not on this chip!\n");
goto err_out;
}
break;
default:
- dev_err(&client->dev, "invalid regulator %s\n",
+ dev_err(dev, "invalid regulator %s\n",
pdata->subdevs[i].name);
goto err_out;
}
id = pdata->subdevs[i].id;
- config.dev = &client->dev;
+ config.dev = dev;
config.init_data = pdata->subdevs[i].platform_data;
+ config.of_node = of_node[i];
config.driver_data = max8660;
rdev[i] = regulator_register(&max8660_reg[id], &config);
if (IS_ERR(rdev[i])) {
ret = PTR_ERR(rdev[i]);
- dev_err(&client->dev, "failed to register %s\n",
+ dev_err(dev, "failed to register %s\n",
max8660_reg[id].name);
goto err_unregister;
}
}
static const struct i2c_device_id max8660_id[] = {
- { "max8660", 0 },
- { "max8661", 0 },
+ { .name = "max8660", .driver_data = MAX8660 },
+ { .name = "max8661", .driver_data = MAX8661 },
{ }
};
MODULE_DEVICE_TABLE(i2c, max8660_id);
static int max8925_regulator_probe(struct platform_device *pdev)
{
struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
- struct regulator_init_data *pdata = pdev->dev.platform_data;
+ struct regulator_init_data *pdata = dev_get_platdata(&pdev->dev);
struct regulator_config config = { };
struct max8925_regulator_info *ri;
struct resource *res;
const struct i2c_device_id *i2c_id)
{
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct max8952_platform_data *pdata = client->dev.platform_data;
+ struct max8952_platform_data *pdata = dev_get_platdata(&client->dev);
struct regulator_config config = { };
struct max8952_data *max8952;
struct max8973_chip *max;
int ret;
- pdata = client->dev.platform_data;
+ pdata = dev_get_platdata(&client->dev);
if (!pdata && !client->dev.of_node) {
dev_err(&client->dev, "No Platform data");
{
const __be32 *min_uV, *max_uV, *uV_offset;
const __be32 *min_uA, *max_uA, *ramp_delay;
+ struct property *prop;
struct regulation_constraints *constraints = &(*init_data)->constraints;
constraints->name = of_get_property(np, "regulator-name", NULL);
if (of_property_read_bool(np, "regulator-allow-bypass"))
constraints->valid_ops_mask |= REGULATOR_CHANGE_BYPASS;
- ramp_delay = of_get_property(np, "regulator-ramp-delay", NULL);
- if (ramp_delay)
- constraints->ramp_delay = be32_to_cpu(*ramp_delay);
+ prop = of_find_property(np, "regulator-ramp-delay", NULL);
+ if (prop && prop->value) {
+ ramp_delay = prop->value;
+ if (*ramp_delay)
+ constraints->ramp_delay = be32_to_cpu(*ramp_delay);
+ else
+ constraints->ramp_disable = true;
+ }
}
/**
.ctrl_addr = PALMAS_SMPS9_CTRL,
},
{
- .name = "SMPS10",
+ .name = "SMPS10_OUT2",
.sname = "smps10-in",
.ctrl_addr = PALMAS_SMPS10_CTRL,
},
+ {
+ .name = "SMPS10_OUT1",
+ .sname = "smps10-out2",
+ .ctrl_addr = PALMAS_SMPS10_CTRL,
+ },
{
.name = "LDO1",
.sname = "ldo1-in",
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
+ .set_bypass = regulator_set_bypass_regmap,
+ .get_bypass = regulator_get_bypass_regmap,
};
static int palmas_is_enabled_ldo(struct regulator_dev *dev)
return ret;
switch (id) {
- case PALMAS_REG_SMPS10:
+ case PALMAS_REG_SMPS10_OUT1:
+ case PALMAS_REG_SMPS10_OUT2:
reg &= ~PALMAS_SMPS10_CTRL_MODE_SLEEP_MASK;
if (reg_init->mode_sleep)
reg |= reg_init->mode_sleep <<
{ .name = "smps7", },
{ .name = "smps8", },
{ .name = "smps9", },
- { .name = "smps10", },
+ { .name = "smps10_out2", },
+ { .name = "smps10_out1", },
{ .name = "ldo1", },
{ .name = "ldo2", },
{ .name = "ldo3", },
static int palmas_regulators_probe(struct platform_device *pdev)
{
struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
- struct palmas_pmic_platform_data *pdata = pdev->dev.platform_data;
+ struct palmas_pmic_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct device_node *node = pdev->dev.of_node;
struct regulator_dev *rdev;
struct regulator_config config = { };
continue;
ramp_delay_support = true;
break;
- case PALMAS_REG_SMPS10:
+ case PALMAS_REG_SMPS10_OUT1:
+ case PALMAS_REG_SMPS10_OUT2:
if (!PALMAS_PMIC_HAS(palmas, SMPS10_BOOST))
continue;
}
pmic->desc[id].id = id;
switch (id) {
- case PALMAS_REG_SMPS10:
+ case PALMAS_REG_SMPS10_OUT1:
+ case PALMAS_REG_SMPS10_OUT2:
pmic->desc[id].n_voltages = PALMAS_SMPS10_NUM_VOLTAGES;
pmic->desc[id].ops = &palmas_ops_smps10;
pmic->desc[id].vsel_reg =
pmic->desc[id].enable_reg =
PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE,
PALMAS_SMPS10_CTRL);
- pmic->desc[id].enable_mask = SMPS10_BOOST_EN;
+ if (id == PALMAS_REG_SMPS10_OUT1)
+ pmic->desc[id].enable_mask = SMPS10_SWITCH_EN;
+ else
+ pmic->desc[id].enable_mask = SMPS10_BOOST_EN;
+ pmic->desc[id].bypass_reg =
+ PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE,
+ PALMAS_SMPS10_CTRL);
+ pmic->desc[id].bypass_mask = SMPS10_BYPASS_EN;
pmic->desc[id].min_uV = 3750000;
pmic->desc[id].uV_step = 1250000;
break;
struct regulator_config config = { };
config.dev = &pdev->dev;
- config.init_data = pdev->dev.platform_data;
+ config.init_data = dev_get_platdata(&pdev->dev);
config.driver_data = pcap;
rdev = regulator_register(&pcap_regulators[pdev->id], &config);
pcf = dev_to_pcf50633(pdev->dev.parent);
config.dev = &pdev->dev;
- config.init_data = pdev->dev.platform_data;
+ config.init_data = dev_get_platdata(&pdev->dev);
config.driver_data = pcf;
config.regmap = pcf->regmap;
--- /dev/null
+/*
+ * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/pfuze100.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+
+#define PFUZE_NUMREGS 128
+#define PFUZE100_VOL_OFFSET 0
+#define PFUZE100_STANDBY_OFFSET 1
+#define PFUZE100_MODE_OFFSET 3
+#define PFUZE100_CONF_OFFSET 4
+
+#define PFUZE100_DEVICEID 0x0
+#define PFUZE100_REVID 0x3
+#define PFUZE100_FABID 0x3
+
+#define PFUZE100_SW1ABVOL 0x20
+#define PFUZE100_SW1CVOL 0x2e
+#define PFUZE100_SW2VOL 0x35
+#define PFUZE100_SW3AVOL 0x3c
+#define PFUZE100_SW3BVOL 0x43
+#define PFUZE100_SW4VOL 0x4a
+#define PFUZE100_SWBSTCON1 0x66
+#define PFUZE100_VREFDDRCON 0x6a
+#define PFUZE100_VSNVSVOL 0x6b
+#define PFUZE100_VGEN1VOL 0x6c
+#define PFUZE100_VGEN2VOL 0x6d
+#define PFUZE100_VGEN3VOL 0x6e
+#define PFUZE100_VGEN4VOL 0x6f
+#define PFUZE100_VGEN5VOL 0x70
+#define PFUZE100_VGEN6VOL 0x71
+
+struct pfuze_regulator {
+ struct regulator_desc desc;
+ unsigned char stby_reg;
+ unsigned char stby_mask;
+};
+
+struct pfuze_chip {
+ struct regmap *regmap;
+ struct device *dev;
+ struct pfuze_regulator regulator_descs[PFUZE100_MAX_REGULATOR];
+ struct regulator_dev *regulators[PFUZE100_MAX_REGULATOR];
+};
+
+static const int pfuze100_swbst[] = {
+ 5000000, 5050000, 5100000, 5150000,
+};
+
+static const int pfuze100_vsnvs[] = {
+ 1000000, 1100000, 1200000, 1300000, 1500000, 1800000, 3000000,
+};
+
+static const struct i2c_device_id pfuze_device_id[] = {
+ {.name = "pfuze100"},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, pfuze_device_id);
+
+static const struct of_device_id pfuze_dt_ids[] = {
+ { .compatible = "fsl,pfuze100" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, pfuze_dt_ids);
+
+static int pfuze100_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
+{
+ struct pfuze_chip *pfuze100 = rdev_get_drvdata(rdev);
+ int id = rdev->desc->id;
+ unsigned int ramp_bits;
+ int ret;
+
+ if (id < PFUZE100_SWBST) {
+ ramp_delay = 12500 / ramp_delay;
+ ramp_bits = (ramp_delay >> 1) - (ramp_delay >> 3);
+ ret = regmap_update_bits(pfuze100->regmap,
+ rdev->desc->vsel_reg + 4,
+ 0xc0, ramp_bits << 6);
+ if (ret < 0)
+ dev_err(pfuze100->dev, "ramp failed, err %d\n", ret);
+ } else
+ ret = -EACCES;
+
+ return ret;
+}
+
+static struct regulator_ops pfuze100_ldo_regulator_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+};
+
+static struct regulator_ops pfuze100_fixed_regulator_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+};
+
+static struct regulator_ops pfuze100_sw_regulator_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .set_ramp_delay = pfuze100_set_ramp_delay,
+};
+
+static struct regulator_ops pfuze100_swb_regulator_ops = {
+ .list_voltage = regulator_list_voltage_table,
+ .map_voltage = regulator_map_voltage_ascend,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+
+};
+
+#define PFUZE100_FIXED_REG(_name, base, voltage) \
+ [PFUZE100_ ## _name] = { \
+ .desc = { \
+ .name = #_name, \
+ .n_voltages = 1, \
+ .ops = &pfuze100_fixed_regulator_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = PFUZE100_ ## _name, \
+ .owner = THIS_MODULE, \
+ .min_uV = (voltage), \
+ .enable_reg = (base), \
+ .enable_mask = 0x10, \
+ }, \
+ }
+
+#define PFUZE100_SW_REG(_name, base, min, max, step) \
+ [PFUZE100_ ## _name] = { \
+ .desc = { \
+ .name = #_name,\
+ .n_voltages = ((max) - (min)) / (step) + 1, \
+ .ops = &pfuze100_sw_regulator_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = PFUZE100_ ## _name, \
+ .owner = THIS_MODULE, \
+ .min_uV = (min), \
+ .uV_step = (step), \
+ .vsel_reg = (base) + PFUZE100_VOL_OFFSET, \
+ .vsel_mask = 0x3f, \
+ }, \
+ .stby_reg = (base) + PFUZE100_STANDBY_OFFSET, \
+ .stby_mask = 0x3f, \
+ }
+
+#define PFUZE100_SWB_REG(_name, base, mask, voltages) \
+ [PFUZE100_ ## _name] = { \
+ .desc = { \
+ .name = #_name, \
+ .n_voltages = ARRAY_SIZE(voltages), \
+ .ops = &pfuze100_swb_regulator_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = PFUZE100_ ## _name, \
+ .owner = THIS_MODULE, \
+ .volt_table = voltages, \
+ .vsel_reg = (base), \
+ .vsel_mask = (mask), \
+ }, \
+ }
+
+#define PFUZE100_VGEN_REG(_name, base, min, max, step) \
+ [PFUZE100_ ## _name] = { \
+ .desc = { \
+ .name = #_name, \
+ .n_voltages = ((max) - (min)) / (step) + 1, \
+ .ops = &pfuze100_ldo_regulator_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = PFUZE100_ ## _name, \
+ .owner = THIS_MODULE, \
+ .min_uV = (min), \
+ .uV_step = (step), \
+ .vsel_reg = (base), \
+ .vsel_mask = 0xf, \
+ .enable_reg = (base), \
+ .enable_mask = 0x10, \
+ }, \
+ .stby_reg = (base), \
+ .stby_mask = 0x20, \
+ }
+
+static struct pfuze_regulator pfuze100_regulators[] = {
+ PFUZE100_SW_REG(SW1AB, PFUZE100_SW1ABVOL, 300000, 1875000, 25000),
+ PFUZE100_SW_REG(SW1C, PFUZE100_SW1CVOL, 300000, 1875000, 25000),
+ PFUZE100_SW_REG(SW2, PFUZE100_SW2VOL, 400000, 1975000, 25000),
+ PFUZE100_SW_REG(SW3A, PFUZE100_SW3AVOL, 400000, 1975000, 25000),
+ PFUZE100_SW_REG(SW3B, PFUZE100_SW3BVOL, 400000, 1975000, 25000),
+ PFUZE100_SW_REG(SW4, PFUZE100_SW4VOL, 400000, 1975000, 25000),
+ PFUZE100_SWB_REG(SWBST, PFUZE100_SWBSTCON1, 0x3 , pfuze100_swbst),
+ PFUZE100_SWB_REG(VSNVS, PFUZE100_VSNVSVOL, 0x7, pfuze100_vsnvs),
+ PFUZE100_FIXED_REG(VREFDDR, PFUZE100_VREFDDRCON, 750000),
+ PFUZE100_VGEN_REG(VGEN1, PFUZE100_VGEN1VOL, 800000, 1550000, 50000),
+ PFUZE100_VGEN_REG(VGEN2, PFUZE100_VGEN2VOL, 800000, 1550000, 50000),
+ PFUZE100_VGEN_REG(VGEN3, PFUZE100_VGEN3VOL, 1800000, 3300000, 100000),
+ PFUZE100_VGEN_REG(VGEN4, PFUZE100_VGEN4VOL, 1800000, 3300000, 100000),
+ PFUZE100_VGEN_REG(VGEN5, PFUZE100_VGEN5VOL, 1800000, 3300000, 100000),
+ PFUZE100_VGEN_REG(VGEN6, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000),
+};
+
+#ifdef CONFIG_OF
+static struct of_regulator_match pfuze100_matches[] = {
+ { .name = "sw1ab", },
+ { .name = "sw1c", },
+ { .name = "sw2", },
+ { .name = "sw3a", },
+ { .name = "sw3b", },
+ { .name = "sw4", },
+ { .name = "swbst", },
+ { .name = "vsnvs", },
+ { .name = "vrefddr", },
+ { .name = "vgen1", },
+ { .name = "vgen2", },
+ { .name = "vgen3", },
+ { .name = "vgen4", },
+ { .name = "vgen5", },
+ { .name = "vgen6", },
+};
+
+static int pfuze_parse_regulators_dt(struct pfuze_chip *chip)
+{
+ struct device *dev = chip->dev;
+ struct device_node *np, *parent;
+ int ret;
+
+ np = of_node_get(dev->parent->of_node);
+ if (!np)
+ return 0;
+
+ parent = of_find_node_by_name(np, "regulators");
+ if (!parent) {
+ dev_err(dev, "regulators node not found\n");
+ return -EINVAL;
+ }
+
+ ret = of_regulator_match(dev, parent, pfuze100_matches,
+ ARRAY_SIZE(pfuze100_matches));
+
+ of_node_put(parent);
+ if (ret < 0) {
+ dev_err(dev, "Error parsing regulator init data: %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline struct regulator_init_data *match_init_data(int index)
+{
+ return pfuze100_matches[index].init_data;
+}
+
+static inline struct device_node *match_of_node(int index)
+{
+ return pfuze100_matches[index].of_node;
+}
+#else
+static int pfuze_parse_regulators_dt(struct pfuze_chip *chip)
+{
+ return 0;
+}
+
+static inline struct regulator_init_data *match_init_data(int index)
+{
+ return NULL;
+}
+
+static inline struct device_node *match_of_node(int index)
+{
+ return NULL;
+}
+#endif
+
+static int pfuze_identify(struct pfuze_chip *pfuze_chip)
+{
+ unsigned int value;
+ int ret;
+
+ ret = regmap_read(pfuze_chip->regmap, PFUZE100_DEVICEID, &value);
+ if (ret)
+ return ret;
+
+ if (value & 0x0f) {
+ dev_warn(pfuze_chip->dev, "Illegal ID: %x\n", value);
+ return -ENODEV;
+ }
+
+ ret = regmap_read(pfuze_chip->regmap, PFUZE100_REVID, &value);
+ if (ret)
+ return ret;
+ dev_info(pfuze_chip->dev,
+ "Full lay: %x, Metal lay: %x\n",
+ (value & 0xf0) >> 4, value & 0x0f);
+
+ ret = regmap_read(pfuze_chip->regmap, PFUZE100_FABID, &value);
+ if (ret)
+ return ret;
+ dev_info(pfuze_chip->dev, "FAB: %x, FIN: %x\n",
+ (value & 0xc) >> 2, value & 0x3);
+
+ return 0;
+}
+
+static const struct regmap_config pfuze_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = PFUZE_NUMREGS - 1,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int pfuze100_regulator_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pfuze_chip *pfuze_chip;
+ struct pfuze_regulator_platform_data *pdata =
+ dev_get_platdata(&client->dev);
+ struct regulator_config config = { };
+ int i, ret;
+
+ pfuze_chip = devm_kzalloc(&client->dev, sizeof(*pfuze_chip),
+ GFP_KERNEL);
+ if (!pfuze_chip)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, pfuze_chip);
+
+ memcpy(pfuze_chip->regulator_descs, pfuze100_regulators,
+ sizeof(pfuze_chip->regulator_descs));
+
+ pfuze_chip->dev = &client->dev;
+
+ pfuze_chip->regmap = devm_regmap_init_i2c(client, &pfuze_regmap_config);
+ if (IS_ERR(pfuze_chip->regmap)) {
+ ret = PTR_ERR(pfuze_chip->regmap);
+ dev_err(&client->dev,
+ "regmap allocation failed with err %d\n", ret);
+ return ret;
+ }
+
+ ret = pfuze_identify(pfuze_chip);
+ if (ret) {
+ dev_err(&client->dev, "unrecognized pfuze chip ID!\n");
+ return ret;
+ }
+
+ ret = pfuze_parse_regulators_dt(pfuze_chip);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < PFUZE100_MAX_REGULATOR; i++) {
+ struct regulator_init_data *init_data;
+ struct regulator_desc *desc;
+ int val;
+
+ desc = &pfuze_chip->regulator_descs[i].desc;
+
+ if (pdata)
+ init_data = pdata->init_data[i];
+ else
+ init_data = match_init_data(i);
+
+ /* SW2~SW4 high bit check and modify the voltage value table */
+ if (i > PFUZE100_SW1C && i < PFUZE100_SWBST) {
+ regmap_read(pfuze_chip->regmap, desc->vsel_reg, &val);
+ if (val & 0x40) {
+ desc->min_uV = 800000;
+ desc->uV_step = 50000;
+ desc->n_voltages = 51;
+ }
+ }
+
+ config.dev = &client->dev;
+ config.init_data = init_data;
+ config.driver_data = pfuze_chip;
+ config.of_node = match_of_node(i);
+
+ pfuze_chip->regulators[i] = regulator_register(desc, &config);
+ if (IS_ERR(pfuze_chip->regulators[i])) {
+ dev_err(&client->dev, "register regulator%s failed\n",
+ pfuze100_regulators[i].desc.name);
+ ret = PTR_ERR(pfuze_chip->regulators[i]);
+ while (--i >= 0)
+ regulator_unregister(pfuze_chip->regulators[i]);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int pfuze100_regulator_remove(struct i2c_client *client)
+{
+ int i;
+ struct pfuze_chip *pfuze_chip = i2c_get_clientdata(client);
+
+ for (i = 0; i < PFUZE100_MAX_REGULATOR; i++)
+ regulator_unregister(pfuze_chip->regulators[i]);
+
+ return 0;
+}
+
+static struct i2c_driver pfuze_driver = {
+ .id_table = pfuze_device_id,
+ .driver = {
+ .name = "pfuze100-regulator",
+ .owner = THIS_MODULE,
+ .of_match_table = pfuze_dt_ids,
+ },
+ .probe = pfuze100_regulator_probe,
+ .remove = pfuze100_regulator_remove,
+};
+module_i2c_driver(pfuze_driver);
+
+MODULE_AUTHOR("Robin Gong <b38343@freescale.com>");
+MODULE_DESCRIPTION("Regulator Driver for Freescale PFUZE100 PMIC");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("i2c:pfuze100-regulator");
#include <linux/gpio.h>
#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
#include <linux/mfd/samsung/core.h>
#include <linux/mfd/samsung/s2mps11.h>
+#define S2MPS11_REGULATOR_CNT ARRAY_SIZE(regulators)
+
struct s2mps11_info {
struct regulator_dev *rdev[S2MPS11_REGULATOR_MAX];
int ramp_delay16;
int ramp_delay7810;
int ramp_delay9;
-
- bool buck6_ramp;
- bool buck2_ramp;
- bool buck3_ramp;
- bool buck4_ramp;
};
static int get_ramp_delay(int ramp_delay)
break;
cnt++;
}
+
+ if (cnt > 3)
+ cnt = 3;
+
return cnt;
}
+static int s2mps11_regulator_set_voltage_time_sel(struct regulator_dev *rdev,
+ unsigned int old_selector,
+ unsigned int new_selector)
+{
+ struct s2mps11_info *s2mps11 = rdev_get_drvdata(rdev);
+ unsigned int ramp_delay = 0;
+ int old_volt, new_volt;
+
+ switch (rdev->desc->id) {
+ case S2MPS11_BUCK2:
+ ramp_delay = s2mps11->ramp_delay2;
+ break;
+ case S2MPS11_BUCK3:
+ ramp_delay = s2mps11->ramp_delay34;
+ break;
+ case S2MPS11_BUCK4:
+ ramp_delay = s2mps11->ramp_delay34;
+ break;
+ case S2MPS11_BUCK5:
+ ramp_delay = s2mps11->ramp_delay5;
+ break;
+ case S2MPS11_BUCK6:
+ case S2MPS11_BUCK1:
+ ramp_delay = s2mps11->ramp_delay16;
+ break;
+ case S2MPS11_BUCK7:
+ case S2MPS11_BUCK8:
+ case S2MPS11_BUCK10:
+ ramp_delay = s2mps11->ramp_delay7810;
+ break;
+ case S2MPS11_BUCK9:
+ ramp_delay = s2mps11->ramp_delay9;
+ }
+
+ if (ramp_delay == 0)
+ ramp_delay = rdev->desc->ramp_delay;
+
+ old_volt = rdev->desc->min_uV + (rdev->desc->uV_step * old_selector);
+ new_volt = rdev->desc->min_uV + (rdev->desc->uV_step * new_selector);
+
+ return DIV_ROUND_UP(abs(new_volt - old_volt), ramp_delay);
+}
+
+static int s2mps11_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
+{
+ struct s2mps11_info *s2mps11 = rdev_get_drvdata(rdev);
+ unsigned int ramp_val, ramp_shift, ramp_reg = S2MPS11_REG_RAMP_BUCK;
+ unsigned int ramp_enable = 1, enable_shift = 0;
+ int ret;
+
+ switch (rdev->desc->id) {
+ case S2MPS11_BUCK1:
+ if (ramp_delay > s2mps11->ramp_delay16)
+ s2mps11->ramp_delay16 = ramp_delay;
+ else
+ ramp_delay = s2mps11->ramp_delay16;
+
+ ramp_shift = S2MPS11_BUCK16_RAMP_SHIFT;
+ break;
+ case S2MPS11_BUCK2:
+ enable_shift = S2MPS11_BUCK2_RAMP_EN_SHIFT;
+ if (!ramp_delay) {
+ ramp_enable = 0;
+ break;
+ }
+
+ s2mps11->ramp_delay2 = ramp_delay;
+ ramp_shift = S2MPS11_BUCK2_RAMP_SHIFT;
+ ramp_reg = S2MPS11_REG_RAMP;
+ break;
+ case S2MPS11_BUCK3:
+ enable_shift = S2MPS11_BUCK3_RAMP_EN_SHIFT;
+ if (!ramp_delay) {
+ ramp_enable = 0;
+ break;
+ }
+
+ if (ramp_delay > s2mps11->ramp_delay34)
+ s2mps11->ramp_delay34 = ramp_delay;
+ else
+ ramp_delay = s2mps11->ramp_delay34;
+
+ ramp_shift = S2MPS11_BUCK34_RAMP_SHIFT;
+ ramp_reg = S2MPS11_REG_RAMP;
+ break;
+ case S2MPS11_BUCK4:
+ enable_shift = S2MPS11_BUCK4_RAMP_EN_SHIFT;
+ if (!ramp_delay) {
+ ramp_enable = 0;
+ break;
+ }
+
+ if (ramp_delay > s2mps11->ramp_delay34)
+ s2mps11->ramp_delay34 = ramp_delay;
+ else
+ ramp_delay = s2mps11->ramp_delay34;
+
+ ramp_shift = S2MPS11_BUCK34_RAMP_SHIFT;
+ ramp_reg = S2MPS11_REG_RAMP;
+ break;
+ case S2MPS11_BUCK5:
+ s2mps11->ramp_delay5 = ramp_delay;
+ ramp_shift = S2MPS11_BUCK5_RAMP_SHIFT;
+ break;
+ case S2MPS11_BUCK6:
+ enable_shift = S2MPS11_BUCK6_RAMP_EN_SHIFT;
+ if (!ramp_delay) {
+ ramp_enable = 0;
+ break;
+ }
+
+ if (ramp_delay > s2mps11->ramp_delay16)
+ s2mps11->ramp_delay16 = ramp_delay;
+ else
+ ramp_delay = s2mps11->ramp_delay16;
+
+ ramp_shift = S2MPS11_BUCK16_RAMP_SHIFT;
+ break;
+ case S2MPS11_BUCK7:
+ case S2MPS11_BUCK8:
+ case S2MPS11_BUCK10:
+ if (ramp_delay > s2mps11->ramp_delay7810)
+ s2mps11->ramp_delay7810 = ramp_delay;
+ else
+ ramp_delay = s2mps11->ramp_delay7810;
+
+ ramp_shift = S2MPS11_BUCK7810_RAMP_SHIFT;
+ break;
+ case S2MPS11_BUCK9:
+ s2mps11->ramp_delay9 = ramp_delay;
+ ramp_shift = S2MPS11_BUCK9_RAMP_SHIFT;
+ break;
+ default:
+ return 0;
+ }
+
+ if (!ramp_enable)
+ goto ramp_disable;
+
+ if (enable_shift) {
+ ret = regmap_update_bits(rdev->regmap, S2MPS11_REG_RAMP,
+ 1 << enable_shift, 1 << enable_shift);
+ if (ret) {
+ dev_err(&rdev->dev, "failed to enable ramp rate\n");
+ return ret;
+ }
+ }
+
+ ramp_val = get_ramp_delay(ramp_delay);
+
+ return regmap_update_bits(rdev->regmap, ramp_reg, 0x3 << ramp_shift,
+ ramp_val << ramp_shift);
+
+ramp_disable:
+ return regmap_update_bits(rdev->regmap, S2MPS11_REG_RAMP,
+ 1 << enable_shift, 0);
+}
+
static struct regulator_ops s2mps11_ldo_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.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_voltage_time_sel = s2mps11_regulator_set_voltage_time_sel,
+ .set_ramp_delay = s2mps11_set_ramp_delay,
};
#define regulator_desc_ldo1(num) { \
{
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[S2MPS11_REGULATOR_MAX];
+ struct device_node *reg_np = NULL;
struct regulator_config config = { };
struct s2mps11_info *s2mps11;
int i, ret;
- unsigned char ramp_enable, ramp_reg = 0;
-
- if (!pdata) {
- dev_err(pdev->dev.parent, "Platform data not supplied\n");
- return -ENODEV;
- }
s2mps11 = devm_kzalloc(&pdev->dev, sizeof(struct s2mps11_info),
GFP_KERNEL);
if (!s2mps11)
return -ENOMEM;
- platform_set_drvdata(pdev, s2mps11);
+ if (!iodev->dev->of_node) {
+ if (pdata) {
+ goto common_reg;
+ } else {
+ dev_err(pdev->dev.parent,
+ "Platform data or DT node not supplied\n");
+ return -ENODEV;
+ }
+ }
- s2mps11->ramp_delay2 = pdata->buck2_ramp_delay;
- s2mps11->ramp_delay34 = pdata->buck34_ramp_delay;
- s2mps11->ramp_delay5 = pdata->buck5_ramp_delay;
- s2mps11->ramp_delay16 = pdata->buck16_ramp_delay;
- s2mps11->ramp_delay7810 = pdata->buck7810_ramp_delay;
- s2mps11->ramp_delay9 = pdata->buck9_ramp_delay;
-
- s2mps11->buck6_ramp = pdata->buck6_ramp_enable;
- s2mps11->buck2_ramp = pdata->buck2_ramp_enable;
- s2mps11->buck3_ramp = pdata->buck3_ramp_enable;
- s2mps11->buck4_ramp = pdata->buck4_ramp_enable;
-
- ramp_enable = (s2mps11->buck2_ramp << 3) | (s2mps11->buck3_ramp << 2) |
- (s2mps11->buck4_ramp << 1) | s2mps11->buck6_ramp ;
-
- if (ramp_enable) {
- if (s2mps11->buck2_ramp)
- ramp_reg |= get_ramp_delay(s2mps11->ramp_delay2) << 6;
- if (s2mps11->buck3_ramp || s2mps11->buck4_ramp)
- ramp_reg |= get_ramp_delay(s2mps11->ramp_delay34) << 4;
- sec_reg_write(iodev, S2MPS11_REG_RAMP, ramp_reg | ramp_enable);
+ for (i = 0; i < S2MPS11_REGULATOR_CNT; i++)
+ rdata[i].name = regulators[i].name;
+
+ reg_np = of_find_node_by_name(iodev->dev->of_node, "regulators");
+ if (!reg_np) {
+ dev_err(&pdev->dev, "could not find regulators sub-node\n");
+ return -EINVAL;
}
- ramp_reg &= 0x00;
- ramp_reg |= get_ramp_delay(s2mps11->ramp_delay5) << 6;
- ramp_reg |= get_ramp_delay(s2mps11->ramp_delay16) << 4;
- ramp_reg |= get_ramp_delay(s2mps11->ramp_delay7810) << 2;
- ramp_reg |= get_ramp_delay(s2mps11->ramp_delay9);
- sec_reg_write(iodev, S2MPS11_REG_RAMP_BUCK, ramp_reg);
+ of_regulator_match(&pdev->dev, reg_np, rdata, S2MPS11_REGULATOR_MAX);
- for (i = 0; i < S2MPS11_REGULATOR_MAX; i++) {
+common_reg:
+ platform_set_drvdata(pdev, s2mps11);
- config.dev = &pdev->dev;
- config.regmap = iodev->regmap;
- config.init_data = pdata->regulators[i].initdata;
- config.driver_data = s2mps11;
+ config.dev = &pdev->dev;
+ config.regmap = iodev->regmap;
+ config.driver_data = s2mps11;
+ for (i = 0; i < S2MPS11_REGULATOR_MAX; i++) {
+ if (!reg_np) {
+ config.init_data = pdata->regulators[i].initdata;
+ } else {
+ config.init_data = rdata[i].init_data;
+ config.of_node = rdata[i].of_node;
+ }
s2mps11->rdev[i] = regulator_register(®ulators[i], &config);
if (IS_ERR(s2mps11->rdev[i])) {
/* Map ABB resources */
pname = "base-address";
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname);
- if (!res) {
- dev_err(dev, "Missing '%s' IO resource\n", pname);
- ret = -ENODEV;
- goto err;
- }
abb->base = devm_ioremap_resource(dev, res);
if (IS_ERR(abb->base)) {
ret = PTR_ERR(abb->base);
pname = "ldo-address";
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, pname);
- if (!res) {
- dev_dbg(dev, "Missing '%s' IO resource\n", pname);
- ret = -ENODEV;
- goto skip_opt;
- }
abb->ldo_base = devm_ioremap_resource(dev, res);
if (IS_ERR(abb->ldo_base)) {
ret = PTR_ERR(abb->ldo_base);
}
}
- pdata = client->dev.platform_data;
+ pdata = dev_get_platdata(&client->dev);
if (!pdata && client->dev.of_node)
pdata = of_get_tps51632_platform_data(&client->dev);
if (!pdata) {
int i;
int chip_id;
- pdata = client->dev.platform_data;
+ pdata = dev_get_platdata(&client->dev);
if (client->dev.of_node) {
const struct of_device_id *match;
* init_data points to array of regulator_init structures
* coming from the board-evm file.
*/
- init_data = client->dev.platform_data;
+ init_data = dev_get_platdata(&client->dev);
if (!init_data)
return -EIO;
#include <linux/regulator/machine.h>
#include <linux/mfd/tps65217.h>
-#define TPS65217_REGULATOR(_name, _id, _ops, _n, _vr, _vm, _em, _t) \
+#define TPS65217_REGULATOR(_name, _id, _ops, _n, _vr, _vm, _em, _t, _lr, _nlr) \
{ \
.name = _name, \
.id = _id, \
.enable_reg = TPS65217_REG_ENABLE, \
.enable_mask = _em, \
.volt_table = _t, \
+ .linear_ranges = _lr, \
+ .n_linear_ranges = _nlr, \
} \
-#define TPS65217_INFO(_nm, _min, _max, _f1, _f2) \
- { \
- .name = _nm, \
- .min_uV = _min, \
- .max_uV = _max, \
- .vsel_to_uv = _f1, \
- .uv_to_vsel = _f2, \
- }
-
static const unsigned int LDO1_VSEL_table[] = {
1000000, 1100000, 1200000, 1250000,
1300000, 1350000, 1400000, 1500000,
2800000, 3000000, 3100000, 3300000,
};
-static int tps65217_vsel_to_uv1(unsigned int vsel)
-{
- int uV = 0;
-
- if (vsel > 63)
- return -EINVAL;
-
- if (vsel <= 24)
- uV = vsel * 25000 + 900000;
- else if (vsel <= 52)
- uV = (vsel - 24) * 50000 + 1500000;
- else if (vsel < 56)
- uV = (vsel - 52) * 100000 + 2900000;
- else
- uV = 3300000;
-
- return uV;
-}
-
-static int tps65217_uv_to_vsel1(int uV, unsigned int *vsel)
-{
- if (uV < 0 || uV > 3300000)
- return -EINVAL;
-
- if (uV <= 1500000)
- *vsel = DIV_ROUND_UP(uV - 900000, 25000);
- else if (uV <= 2900000)
- *vsel = 24 + DIV_ROUND_UP(uV - 1500000, 50000);
- else if (uV < 3300000)
- *vsel = 52 + DIV_ROUND_UP(uV - 2900000, 100000);
- else
- *vsel = 56;
-
- return 0;
-}
-
-static int tps65217_vsel_to_uv2(unsigned int vsel)
-{
- int uV = 0;
-
- if (vsel > 31)
- return -EINVAL;
-
- if (vsel <= 8)
- uV = vsel * 50000 + 1500000;
- else if (vsel <= 13)
- uV = (vsel - 8) * 100000 + 1900000;
- else
- uV = (vsel - 13) * 50000 + 2400000;
-
- return uV;
-}
-
-static int tps65217_uv_to_vsel2(int uV, unsigned int *vsel)
-{
- if (uV < 0 || uV > 3300000)
- return -EINVAL;
-
- if (uV <= 1900000)
- *vsel = DIV_ROUND_UP(uV - 1500000, 50000);
- else if (uV <= 2400000)
- *vsel = 8 + DIV_ROUND_UP(uV - 1900000, 100000);
- else
- *vsel = 13 + DIV_ROUND_UP(uV - 2400000, 50000);
-
- return 0;
-}
+static const struct regulator_linear_range tps65217_uv1_ranges[] = {
+ { .min_uV = 900000, .max_uV = 1500000, .min_sel = 0, .max_sel = 24,
+ .uV_step = 25000 },
+ { .min_uV = 1550000, .max_uV = 1800000, .min_sel = 25, .max_sel = 30,
+ .uV_step = 50000 },
+ { .min_uV = 1850000, .max_uV = 2900000, .min_sel = 31, .max_sel = 52,
+ .uV_step = 50000 },
+ { .min_uV = 3000000, .max_uV = 3200000, .min_sel = 53, .max_sel = 55,
+ .uV_step = 100000 },
+ { .min_uV = 3300000, .max_uV = 3300000, .min_sel = 56, .max_sel = 62,
+ .uV_step = 0 },
+};
-static struct tps_info tps65217_pmic_regs[] = {
- TPS65217_INFO("DCDC1", 900000, 1800000, tps65217_vsel_to_uv1,
- tps65217_uv_to_vsel1),
- TPS65217_INFO("DCDC2", 900000, 3300000, tps65217_vsel_to_uv1,
- tps65217_uv_to_vsel1),
- TPS65217_INFO("DCDC3", 900000, 1500000, tps65217_vsel_to_uv1,
- tps65217_uv_to_vsel1),
- TPS65217_INFO("LDO1", 1000000, 3300000, NULL, NULL),
- TPS65217_INFO("LDO2", 900000, 3300000, tps65217_vsel_to_uv1,
- tps65217_uv_to_vsel1),
- TPS65217_INFO("LDO3", 1800000, 3300000, tps65217_vsel_to_uv2,
- tps65217_uv_to_vsel2),
- TPS65217_INFO("LDO4", 1800000, 3300000, tps65217_vsel_to_uv2,
- tps65217_uv_to_vsel2),
+static const struct regulator_linear_range tps65217_uv2_ranges[] = {
+ { .min_uV = 1500000, .max_uV = 1900000, .min_sel = 0, .max_sel = 8,
+ .uV_step = 50000 },
+ { .min_uV = 2000000, .max_uV = 2400000, .min_sel = 9, .max_sel = 13,
+ .uV_step = 100000 },
+ { .min_uV = 2450000, .max_uV = 3300000, .min_sel = 14, .max_sel = 31,
+ .uV_step = 50000 },
};
static int tps65217_pmic_enable(struct regulator_dev *dev)
return ret;
}
-static int tps65217_pmic_map_voltage(struct regulator_dev *dev,
- int min_uV, int max_uV)
-{
-
- struct tps65217 *tps = rdev_get_drvdata(dev);
- unsigned int sel, rid = rdev_get_id(dev);
- int ret;
-
- /* LDO1 uses regulator_map_voltage_iterate() */
- if (rid == TPS65217_LDO_1)
- return -EINVAL;
-
- if (rid < TPS65217_DCDC_1 || rid > TPS65217_LDO_4)
- return -EINVAL;
-
- if (min_uV < tps->info[rid]->min_uV)
- min_uV = tps->info[rid]->min_uV;
-
- if (max_uV < tps->info[rid]->min_uV || min_uV > tps->info[rid]->max_uV)
- return -EINVAL;
-
- ret = tps->info[rid]->uv_to_vsel(min_uV, &sel);
- if (ret)
- return ret;
-
- return sel;
-}
-
-static int tps65217_pmic_list_voltage(struct regulator_dev *dev,
- unsigned selector)
-{
- struct tps65217 *tps = rdev_get_drvdata(dev);
- unsigned int rid = rdev_get_id(dev);
-
- if (rid < TPS65217_DCDC_1 || rid > TPS65217_LDO_4)
- return -EINVAL;
-
- if (selector >= dev->desc->n_voltages)
- return -EINVAL;
-
- return tps->info[rid]->vsel_to_uv(selector);
-}
-
/* Operations permitted on DCDCx, LDO2, LDO3 and LDO4 */
static struct regulator_ops tps65217_pmic_ops = {
.is_enabled = regulator_is_enabled_regmap,
.disable = tps65217_pmic_disable,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = tps65217_pmic_set_voltage_sel,
- .list_voltage = tps65217_pmic_list_voltage,
- .map_voltage = tps65217_pmic_map_voltage,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
};
/* Operations permitted on LDO1 */
static const struct regulator_desc regulators[] = {
TPS65217_REGULATOR("DCDC1", TPS65217_DCDC_1, tps65217_pmic_ops, 64,
TPS65217_REG_DEFDCDC1, TPS65217_DEFDCDCX_DCDC_MASK,
- TPS65217_ENABLE_DC1_EN, NULL),
+ TPS65217_ENABLE_DC1_EN, NULL, tps65217_uv1_ranges,
+ 2), /* DCDC1 voltage range: 900000 ~ 1800000 */
TPS65217_REGULATOR("DCDC2", TPS65217_DCDC_2, tps65217_pmic_ops, 64,
TPS65217_REG_DEFDCDC2, TPS65217_DEFDCDCX_DCDC_MASK,
- TPS65217_ENABLE_DC2_EN, NULL),
+ TPS65217_ENABLE_DC2_EN, NULL, tps65217_uv1_ranges,
+ ARRAY_SIZE(tps65217_uv1_ranges)),
TPS65217_REGULATOR("DCDC3", TPS65217_DCDC_3, tps65217_pmic_ops, 64,
TPS65217_REG_DEFDCDC3, TPS65217_DEFDCDCX_DCDC_MASK,
- TPS65217_ENABLE_DC3_EN, NULL),
+ TPS65217_ENABLE_DC3_EN, NULL, tps65217_uv1_ranges,
+ 1), /* DCDC3 voltage range: 900000 ~ 1500000 */
TPS65217_REGULATOR("LDO1", TPS65217_LDO_1, tps65217_pmic_ldo1_ops, 16,
TPS65217_REG_DEFLDO1, TPS65217_DEFLDO1_LDO1_MASK,
- TPS65217_ENABLE_LDO1_EN, LDO1_VSEL_table),
+ TPS65217_ENABLE_LDO1_EN, LDO1_VSEL_table, NULL, 0),
TPS65217_REGULATOR("LDO2", TPS65217_LDO_2, tps65217_pmic_ops, 64,
TPS65217_REG_DEFLDO2, TPS65217_DEFLDO2_LDO2_MASK,
- TPS65217_ENABLE_LDO2_EN, NULL),
+ TPS65217_ENABLE_LDO2_EN, NULL, tps65217_uv1_ranges,
+ ARRAY_SIZE(tps65217_uv1_ranges)),
TPS65217_REGULATOR("LDO3", TPS65217_LDO_3, tps65217_pmic_ops, 32,
TPS65217_REG_DEFLS1, TPS65217_DEFLDO3_LDO3_MASK,
TPS65217_ENABLE_LS1_EN | TPS65217_DEFLDO3_LDO3_EN,
- NULL),
+ NULL, tps65217_uv2_ranges,
+ ARRAY_SIZE(tps65217_uv2_ranges)),
TPS65217_REGULATOR("LDO4", TPS65217_LDO_4, tps65217_pmic_ops, 32,
TPS65217_REG_DEFLS2, TPS65217_DEFLDO4_LDO4_MASK,
TPS65217_ENABLE_LS2_EN | TPS65217_DEFLDO4_LDO4_EN,
- NULL),
+ NULL, tps65217_uv2_ranges,
+ ARRAY_SIZE(tps65217_uv2_ranges)),
};
#ifdef CONFIG_OF
continue;
/* Register the regulators */
- tps->info[i] = &tps65217_pmic_regs[i];
-
config.dev = tps->dev;
config.init_data = reg_data;
config.driver_data = tps;
struct regulator_config config = { };
int ret = 0, i;
- init_data = dev->platform_data;
+ init_data = dev_get_platdata(dev);
if (!init_data) {
dev_err(dev, "could not find regulator platform data\n");
return -EINVAL;
int eco_reg;
};
+static const struct regulator_linear_range tps65912_ldo_ranges[] = {
+ { .min_uV = 800000, .max_uV = 1600000, .min_sel = 0, .max_sel = 32,
+ .uV_step = 25000 },
+ { .min_uV = 1650000, .max_uV = 3000000, .min_sel = 33, .max_sel = 60,
+ .uV_step = 50000 },
+ { .min_uV = 3100000, .max_uV = 3300000, .min_sel = 61, .max_sel = 63,
+ .uV_step = 100000 },
+};
+
static int tps65912_get_range(struct tps65912_reg *pmic, int id)
{
struct tps65912 *mfd = pmic->mfd;
return uv;
}
-static unsigned long tps65912_vsel_to_uv_ldo(u8 vsel)
-{
- unsigned long uv = 0;
-
- if (vsel <= 32)
- uv = ((vsel * 25000) + 800000);
- else if (vsel > 32 && vsel <= 60)
- uv = (((vsel - 32) * 50000) + 1600000);
- else if (vsel > 60)
- uv = (((vsel - 60) * 100000) + 3000000);
-
- return uv;
-}
-
static int tps65912_get_ctrl_register(int id)
{
if (id >= TPS65912_REG_DCDC1 && id <= TPS65912_REG_LDO4)
struct tps65912_reg *pmic = rdev_get_drvdata(dev);
int range, voltage = 0, id = rdev_get_id(dev);
- if (id >= TPS65912_REG_LDO1 && id <= TPS65912_REG_LDO10)
- return tps65912_vsel_to_uv_ldo(selector);
-
if (id > TPS65912_REG_DCDC4)
return -EINVAL;
.disable = tps65912_reg_disable,
.get_voltage_sel = tps65912_get_voltage_sel,
.set_voltage_sel = tps65912_set_voltage_sel,
- .list_voltage = tps65912_list_voltage,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
};
static int tps65912_probe(struct platform_device *pdev)
pmic->desc[i].name = info->name;
pmic->desc[i].id = i;
pmic->desc[i].n_voltages = 64;
- pmic->desc[i].ops = (i > TPS65912_REG_DCDC4 ?
- &tps65912_ops_ldo : &tps65912_ops_dcdc);
+ if (i > TPS65912_REG_DCDC4) {
+ pmic->desc[i].ops = &tps65912_ops_ldo;
+ pmic->desc[i].linear_ranges = tps65912_ldo_ranges;
+ pmic->desc[i].n_linear_ranges =
+ ARRAY_SIZE(tps65912_ldo_ranges);
+ } else {
+ pmic->desc[i].ops = &tps65912_ops_dcdc;
+ }
pmic->desc[i].type = REGULATOR_VOLTAGE;
pmic->desc[i].owner = THIS_MODULE;
range = tps65912_get_range(pmic, i);
drvdata = NULL;
} else {
id = pdev->id;
- initdata = pdev->dev.platform_data;
+ initdata = dev_get_platdata(&pdev->dev);
for (i = 0, template = NULL; i < ARRAY_SIZE(twl_of_match); i++) {
template = twl_of_match[i].data;
if (template && template->desc.id == id)
struct userspace_consumer_data *drvdata;
int ret;
- pdata = pdev->dev.platform_data;
+ pdata = dev_get_platdata(&pdev->dev);
if (!pdata)
return -EINVAL;
static int regulator_virtual_probe(struct platform_device *pdev)
{
- char *reg_id = pdev->dev.platform_data;
+ char *reg_id = dev_get_platdata(&pdev->dev);
struct virtual_consumer_data *drvdata;
int ret;
static int wm831x_buckv_probe(struct platform_device *pdev)
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
- struct wm831x_pdata *pdata = wm831x->dev->platform_data;
+ struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev);
struct regulator_config config = { };
int id;
struct wm831x_dcdc *dcdc;
static int wm831x_buckp_probe(struct platform_device *pdev)
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
- struct wm831x_pdata *pdata = wm831x->dev->platform_data;
+ struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev);
struct regulator_config config = { };
int id;
struct wm831x_dcdc *dcdc;
static int wm831x_boostp_probe(struct platform_device *pdev)
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
- struct wm831x_pdata *pdata = wm831x->dev->platform_data;
+ struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev);
struct regulator_config config = { };
int id = pdev->id % ARRAY_SIZE(pdata->dcdc);
struct wm831x_dcdc *dcdc;
static int wm831x_epe_probe(struct platform_device *pdev)
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
- struct wm831x_pdata *pdata = wm831x->dev->platform_data;
+ struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev);
struct regulator_config config = { };
int id = pdev->id % ARRAY_SIZE(pdata->epe);
struct wm831x_dcdc *dcdc;
static int wm831x_isink_probe(struct platform_device *pdev)
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
- struct wm831x_pdata *pdata = wm831x->dev->platform_data;
+ struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev);
struct wm831x_isink *isink;
int id = pdev->id % ARRAY_SIZE(pdata->isink);
struct regulator_config config = { };
* General purpose LDOs
*/
-#define WM831X_GP_LDO_SELECTOR_LOW 0xe
-#define WM831X_GP_LDO_MAX_SELECTOR 0x1f
-
-static int wm831x_gp_ldo_list_voltage(struct regulator_dev *rdev,
- unsigned int selector)
-{
- /* 0.9-1.6V in 50mV steps */
- if (selector <= WM831X_GP_LDO_SELECTOR_LOW)
- return 900000 + (selector * 50000);
- /* 1.7-3.3V in 100mV steps */
- if (selector <= WM831X_GP_LDO_MAX_SELECTOR)
- return 1600000 + ((selector - WM831X_GP_LDO_SELECTOR_LOW)
- * 100000);
- return -EINVAL;
-}
-
-static int wm831x_gp_ldo_map_voltage(struct regulator_dev *rdev,
- int min_uV, int max_uV)
-{
- int volt, vsel;
-
- if (min_uV < 900000)
- vsel = 0;
- else if (min_uV < 1700000)
- vsel = ((min_uV - 900000) / 50000);
- else
- vsel = ((min_uV - 1700000) / 100000)
- + WM831X_GP_LDO_SELECTOR_LOW + 1;
-
- volt = wm831x_gp_ldo_list_voltage(rdev, vsel);
- if (volt < min_uV || volt > max_uV)
- return -EINVAL;
-
- return vsel;
-}
+static const struct regulator_linear_range wm831x_gp_ldo_ranges[] = {
+ { .min_uV = 900000, .max_uV = 1650000, .min_sel = 0, .max_sel = 14,
+ .uV_step = 50000 },
+ { .min_uV = 1700000, .max_uV = 3300000, .min_sel = 15, .max_sel = 31,
+ .uV_step = 100000 },
+};
static int wm831x_gp_ldo_set_suspend_voltage(struct regulator_dev *rdev,
int uV)
struct wm831x *wm831x = ldo->wm831x;
int sel, reg = ldo->base + WM831X_LDO_SLEEP_CONTROL;
- sel = wm831x_gp_ldo_map_voltage(rdev, uV, uV);
+ sel = regulator_map_voltage_linear_range(rdev, uV, uV);
if (sel < 0)
return sel;
static struct regulator_ops wm831x_gp_ldo_ops = {
- .list_voltage = wm831x_gp_ldo_list_voltage,
- .map_voltage = wm831x_gp_ldo_map_voltage,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.set_suspend_voltage = wm831x_gp_ldo_set_suspend_voltage,
static int wm831x_gp_ldo_probe(struct platform_device *pdev)
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
- struct wm831x_pdata *pdata = wm831x->dev->platform_data;
+ struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev);
struct regulator_config config = { };
int id;
struct wm831x_ldo *ldo;
ldo->desc.id = id;
ldo->desc.type = REGULATOR_VOLTAGE;
- ldo->desc.n_voltages = WM831X_GP_LDO_MAX_SELECTOR + 1;
+ ldo->desc.n_voltages = 32;
ldo->desc.ops = &wm831x_gp_ldo_ops;
ldo->desc.owner = THIS_MODULE;
ldo->desc.vsel_reg = ldo->base + WM831X_LDO_ON_CONTROL;
ldo->desc.enable_mask = 1 << id;
ldo->desc.bypass_reg = ldo->base;
ldo->desc.bypass_mask = WM831X_LDO1_SWI;
+ ldo->desc.linear_ranges = wm831x_gp_ldo_ranges;
+ ldo->desc.n_linear_ranges = ARRAY_SIZE(wm831x_gp_ldo_ranges);
config.dev = pdev->dev.parent;
if (pdata)
* Analogue LDOs
*/
-
-#define WM831X_ALDO_SELECTOR_LOW 0xc
-#define WM831X_ALDO_MAX_SELECTOR 0x1f
-
-static int wm831x_aldo_list_voltage(struct regulator_dev *rdev,
- unsigned int selector)
-{
- /* 1-1.6V in 50mV steps */
- if (selector <= WM831X_ALDO_SELECTOR_LOW)
- return 1000000 + (selector * 50000);
- /* 1.7-3.5V in 100mV steps */
- if (selector <= WM831X_ALDO_MAX_SELECTOR)
- return 1600000 + ((selector - WM831X_ALDO_SELECTOR_LOW)
- * 100000);
- return -EINVAL;
-}
-
-static int wm831x_aldo_map_voltage(struct regulator_dev *rdev,
- int min_uV, int max_uV)
-{
- int volt, vsel;
-
- if (min_uV < 1000000)
- vsel = 0;
- else if (min_uV < 1700000)
- vsel = ((min_uV - 1000000) / 50000);
- else
- vsel = ((min_uV - 1700000) / 100000)
- + WM831X_ALDO_SELECTOR_LOW + 1;
-
- volt = wm831x_aldo_list_voltage(rdev, vsel);
- if (volt < min_uV || volt > max_uV)
- return -EINVAL;
-
- return vsel;
-
-}
+static const struct regulator_linear_range wm831x_aldo_ranges[] = {
+ { .min_uV = 1000000, .max_uV = 1650000, .min_sel = 0, .max_sel = 12,
+ .uV_step = 50000 },
+ { .min_uV = 1700000, .max_uV = 3500000, .min_sel = 13, .max_sel = 31,
+ .uV_step = 100000 },
+};
static int wm831x_aldo_set_suspend_voltage(struct regulator_dev *rdev,
int uV)
struct wm831x *wm831x = ldo->wm831x;
int sel, reg = ldo->base + WM831X_LDO_SLEEP_CONTROL;
- sel = wm831x_aldo_map_voltage(rdev, uV, uV);
+ sel = regulator_map_voltage_linear_range(rdev, uV, uV);
if (sel < 0)
return sel;
}
static struct regulator_ops wm831x_aldo_ops = {
- .list_voltage = wm831x_aldo_list_voltage,
- .map_voltage = wm831x_aldo_map_voltage,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.set_suspend_voltage = wm831x_aldo_set_suspend_voltage,
static int wm831x_aldo_probe(struct platform_device *pdev)
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
- struct wm831x_pdata *pdata = wm831x->dev->platform_data;
+ struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev);
struct regulator_config config = { };
int id;
struct wm831x_ldo *ldo;
ldo->desc.id = id;
ldo->desc.type = REGULATOR_VOLTAGE;
- ldo->desc.n_voltages = WM831X_ALDO_MAX_SELECTOR + 1;
+ ldo->desc.n_voltages = 32;
+ ldo->desc.linear_ranges = wm831x_aldo_ranges;
+ ldo->desc.n_linear_ranges = ARRAY_SIZE(wm831x_aldo_ranges);
ldo->desc.ops = &wm831x_aldo_ops;
ldo->desc.owner = THIS_MODULE;
ldo->desc.vsel_reg = ldo->base + WM831X_LDO_ON_CONTROL;
static int wm831x_alive_ldo_probe(struct platform_device *pdev)
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
- struct wm831x_pdata *pdata = wm831x->dev->platform_data;
+ struct wm831x_pdata *pdata = dev_get_platdata(wm831x->dev);
struct regulator_config config = { };
int id;
struct wm831x_ldo *ldo;
return 0;
}
-static int wm8350_ldo_list_voltage(struct regulator_dev *rdev,
- unsigned selector)
-{
- if (selector > WM8350_LDO1_VSEL_MASK)
- return -EINVAL;
-
- if (selector < 16)
- return (selector * 50000) + 900000;
- else
- return ((selector - 16) * 100000) + 1800000;
-}
-
-static int wm8350_ldo_map_voltage(struct regulator_dev *rdev, int min_uV,
- int max_uV)
-{
- int volt, sel;
- int min_mV = min_uV / 1000;
- int max_mV = max_uV / 1000;
-
- if (min_mV < 900 || min_mV > 3300)
- return -EINVAL;
- if (max_mV < 900 || max_mV > 3300)
- return -EINVAL;
-
- if (min_mV < 1800) /* step size is 50mV < 1800mV */
- sel = DIV_ROUND_UP(min_uV - 900, 50);
- else /* step size is 100mV > 1800mV */
- sel = DIV_ROUND_UP(min_uV - 1800, 100) + 16;
-
- volt = wm8350_ldo_list_voltage(rdev, sel);
- if (volt < min_uV || volt > max_uV)
- return -EINVAL;
-
- return sel;
-}
+static const struct regulator_linear_range wm8350_ldo_ranges[] = {
+ { .min_uV = 900000, .max_uV = 1750000, .min_sel = 0, .max_sel = 15,
+ .uV_step = 50000 },
+ { .min_uV = 1800000, .max_uV = 3300000, .min_sel = 16, .max_sel = 31,
+ .uV_step = 100000 },
+};
static int wm8350_ldo_set_suspend_voltage(struct regulator_dev *rdev, int uV)
{
return -EINVAL;
}
- sel = wm8350_ldo_map_voltage(rdev, uV, uV);
+ sel = regulator_map_voltage_linear_range(rdev, uV, uV);
if (sel < 0)
return -EINVAL;
};
static struct regulator_ops wm8350_ldo_ops = {
- .map_voltage = wm8350_ldo_map_voltage,
+ .map_voltage = regulator_map_voltage_linear_range,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
- .list_voltage = wm8350_ldo_list_voltage,
+ .list_voltage = regulator_list_voltage_linear_range,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.irq = WM8350_IRQ_UV_LDO1,
.type = REGULATOR_VOLTAGE,
.n_voltages = WM8350_LDO1_VSEL_MASK + 1,
+ .linear_ranges = wm8350_ldo_ranges,
+ .n_linear_ranges = ARRAY_SIZE(wm8350_ldo_ranges),
.vsel_reg = WM8350_LDO1_CONTROL,
.vsel_mask = WM8350_LDO1_VSEL_MASK,
.enable_reg = WM8350_DCDC_LDO_REQUESTED,
.irq = WM8350_IRQ_UV_LDO2,
.type = REGULATOR_VOLTAGE,
.n_voltages = WM8350_LDO2_VSEL_MASK + 1,
+ .linear_ranges = wm8350_ldo_ranges,
+ .n_linear_ranges = ARRAY_SIZE(wm8350_ldo_ranges),
.vsel_reg = WM8350_LDO2_CONTROL,
.vsel_mask = WM8350_LDO2_VSEL_MASK,
.enable_reg = WM8350_DCDC_LDO_REQUESTED,
.irq = WM8350_IRQ_UV_LDO3,
.type = REGULATOR_VOLTAGE,
.n_voltages = WM8350_LDO3_VSEL_MASK + 1,
+ .linear_ranges = wm8350_ldo_ranges,
+ .n_linear_ranges = ARRAY_SIZE(wm8350_ldo_ranges),
.vsel_reg = WM8350_LDO3_CONTROL,
.vsel_mask = WM8350_LDO3_VSEL_MASK,
.enable_reg = WM8350_DCDC_LDO_REQUESTED,
.irq = WM8350_IRQ_UV_LDO4,
.type = REGULATOR_VOLTAGE,
.n_voltages = WM8350_LDO4_VSEL_MASK + 1,
+ .linear_ranges = wm8350_ldo_ranges,
+ .n_linear_ranges = ARRAY_SIZE(wm8350_ldo_ranges),
.vsel_reg = WM8350_LDO4_CONTROL,
.vsel_mask = WM8350_LDO4_VSEL_MASK,
.enable_reg = WM8350_DCDC_LDO_REQUESTED,
}
config.dev = &pdev->dev;
- config.init_data = pdev->dev.platform_data;
+ config.init_data = dev_get_platdata(&pdev->dev);
config.driver_data = dev_get_drvdata(&pdev->dev);
config.regmap = wm8350->regmap;
#include <linux/regulator/driver.h>
#include <linux/mfd/wm8400-private.h>
-static int wm8400_ldo_list_voltage(struct regulator_dev *dev,
- unsigned selector)
-{
- if (selector > WM8400_LDO1_VSEL_MASK)
- return -EINVAL;
-
- if (selector < 15)
- return 900000 + (selector * 50000);
- else
- return 1700000 + ((selector - 15) * 100000);
-}
-
-static int wm8400_ldo_map_voltage(struct regulator_dev *dev,
- int min_uV, int max_uV)
-{
- u16 val;
- int volt;
-
- if (min_uV < 900000 || min_uV > 3300000)
- return -EINVAL;
-
- if (min_uV < 1700000) /* Steps of 50mV from 900mV; */
- val = DIV_ROUND_UP(min_uV - 900000, 50000);
- else /* Steps of 100mV from 1700mV */
- val = DIV_ROUND_UP(min_uV - 1700000, 100000) + 15;
-
- volt = wm8400_ldo_list_voltage(dev, val);
- if (volt < min_uV || volt > max_uV)
- return -EINVAL;
-
- return val;
-}
+static const struct regulator_linear_range wm8400_ldo_ranges[] = {
+ { .min_uV = 900000, .max_uV = 1600000, .min_sel = 0, .max_sel = 14,
+ .uV_step = 50000 },
+ { .min_uV = 1700000, .max_uV = 3300000, .min_sel = 15, .max_sel = 31,
+ .uV_step = 100000 },
+};
static struct regulator_ops wm8400_ldo_ops = {
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
- .list_voltage = wm8400_ldo_list_voltage,
+ .list_voltage = regulator_list_voltage_linear_range,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
- .map_voltage = wm8400_ldo_map_voltage,
+ .map_voltage = regulator_map_voltage_linear_range,
};
static unsigned int wm8400_dcdc_get_mode(struct regulator_dev *dev)
.enable_reg = WM8400_LDO1_CONTROL,
.enable_mask = WM8400_LDO1_ENA,
.n_voltages = WM8400_LDO1_VSEL_MASK + 1,
+ .linear_ranges = wm8400_ldo_ranges,
+ .n_linear_ranges = ARRAY_SIZE(wm8400_ldo_ranges),
.vsel_reg = WM8400_LDO1_CONTROL,
.vsel_mask = WM8400_LDO1_VSEL_MASK,
.type = REGULATOR_VOLTAGE,
.enable_reg = WM8400_LDO2_CONTROL,
.enable_mask = WM8400_LDO2_ENA,
.n_voltages = WM8400_LDO2_VSEL_MASK + 1,
+ .linear_ranges = wm8400_ldo_ranges,
+ .n_linear_ranges = ARRAY_SIZE(wm8400_ldo_ranges),
.type = REGULATOR_VOLTAGE,
.vsel_reg = WM8400_LDO2_CONTROL,
.vsel_mask = WM8400_LDO2_VSEL_MASK,
.enable_reg = WM8400_LDO3_CONTROL,
.enable_mask = WM8400_LDO3_ENA,
.n_voltages = WM8400_LDO3_VSEL_MASK + 1,
+ .linear_ranges = wm8400_ldo_ranges,
+ .n_linear_ranges = ARRAY_SIZE(wm8400_ldo_ranges),
.vsel_reg = WM8400_LDO3_CONTROL,
.vsel_mask = WM8400_LDO3_VSEL_MASK,
.type = REGULATOR_VOLTAGE,
.enable_reg = WM8400_LDO4_CONTROL,
.enable_mask = WM8400_LDO4_ENA,
.n_voltages = WM8400_LDO4_VSEL_MASK + 1,
+ .linear_ranges = wm8400_ldo_ranges,
+ .n_linear_ranges = ARRAY_SIZE(wm8400_ldo_ranges),
.vsel_reg = WM8400_LDO4_CONTROL,
.vsel_mask = WM8400_LDO4_VSEL_MASK,
.type = REGULATOR_VOLTAGE,
struct regulator_dev *rdev;
config.dev = &pdev->dev;
- config.init_data = pdev->dev.platform_data;
+ config.init_data = dev_get_platdata(&pdev->dev);
config.driver_data = wm8400;
config.regmap = wm8400->regmap;
static int wm8994_ldo_probe(struct platform_device *pdev)
{
struct wm8994 *wm8994 = dev_get_drvdata(pdev->dev.parent);
- struct wm8994_pdata *pdata = wm8994->dev->platform_data;
+ struct wm8994_pdata *pdata = dev_get_platdata(wm8994->dev);
int id = pdev->id % ARRAY_SIZE(pdata->ldo);
struct regulator_config config = { };
struct wm8994_ldo *ldo;
if (IS_ERR(devmap))
return PTR_ERR(devmap);
- if ((strict_strtoul(buf, 10, &val) != 0) || val > 1)
+ if ((kstrtoul(buf, 10, &val) != 0) || val > 1)
return -EINVAL;
spin_lock(&dasd_devmap_lock);
if (IS_ERR(device))
return -ENODEV;
- if ((strict_strtoul(buf, 10, &val) != 0) ||
+ if ((kstrtoul(buf, 10, &val) != 0) ||
(val > DASD_EXPIRES_MAX) || val == 0) {
dasd_put_device(device);
return -EINVAL;
if (IS_ERR(device))
return -ENODEV;
- if ((strict_strtoul(buf, 10, &val) != 0) ||
+ if ((kstrtoul(buf, 10, &val) != 0) ||
(val > DASD_RETRIES_MAX)) {
dasd_put_device(device);
return -EINVAL;
if (IS_ERR(device) || !device->block)
return -ENODEV;
- if ((strict_strtoul(buf, 10, &val) != 0) ||
+ if ((kstrtoul(buf, 10, &val) != 0) ||
val > UINT_MAX / HZ) {
dasd_put_device(device);
return -EINVAL;
static struct ccw_driver dasd_eckd_driver; /* see below */
+static void *rawpadpage;
+
#define INIT_CQR_OK 0
#define INIT_CQR_UNFORMATTED 1
#define INIT_CQR_ERROR 2
unsigned int seg_len, len_to_track_end;
unsigned int first_offs;
unsigned int cidaw, cplength, datasize;
- sector_t first_trk, last_trk;
+ sector_t first_trk, last_trk, sectors;
+ sector_t start_padding_sectors, end_sector_offset, end_padding_sectors;
unsigned int pfx_datasize;
/*
* raw track access needs to be mutiple of 64k and on 64k boundary
+ * For read requests we can fix an incorrect alignment by padding
+ * the request with dummy pages.
*/
- if ((blk_rq_pos(req) % DASD_RAW_SECTORS_PER_TRACK) != 0) {
- cqr = ERR_PTR(-EINVAL);
- goto out;
- }
- if (((blk_rq_pos(req) + blk_rq_sectors(req)) %
- DASD_RAW_SECTORS_PER_TRACK) != 0) {
+ start_padding_sectors = blk_rq_pos(req) % DASD_RAW_SECTORS_PER_TRACK;
+ end_sector_offset = (blk_rq_pos(req) + blk_rq_sectors(req)) %
+ DASD_RAW_SECTORS_PER_TRACK;
+ end_padding_sectors = (DASD_RAW_SECTORS_PER_TRACK - end_sector_offset) %
+ DASD_RAW_SECTORS_PER_TRACK;
+ basedev = block->base;
+ if ((start_padding_sectors || end_padding_sectors) &&
+ (rq_data_dir(req) == WRITE)) {
+ DBF_DEV_EVENT(DBF_ERR, basedev,
+ "raw write not track aligned (%lu,%lu) req %p",
+ start_padding_sectors, end_padding_sectors, req);
cqr = ERR_PTR(-EINVAL);
goto out;
}
DASD_RAW_SECTORS_PER_TRACK;
trkcount = last_trk - first_trk + 1;
first_offs = 0;
- basedev = block->base;
if (rq_data_dir(req) == READ)
cmd = DASD_ECKD_CCW_READ_TRACK;
}
idaws = (unsigned long *)(cqr->data + pfx_datasize);
-
len_to_track_end = 0;
-
+ if (start_padding_sectors) {
+ ccw[-1].flags |= CCW_FLAG_CC;
+ ccw->cmd_code = cmd;
+ /* maximum 3390 track size */
+ ccw->count = 57326;
+ /* 64k map to one track */
+ len_to_track_end = 65536 - start_padding_sectors * 512;
+ ccw->cda = (__u32)(addr_t)idaws;
+ ccw->flags |= CCW_FLAG_IDA;
+ ccw->flags |= CCW_FLAG_SLI;
+ ccw++;
+ for (sectors = 0; sectors < start_padding_sectors; sectors += 8)
+ idaws = idal_create_words(idaws, rawpadpage, PAGE_SIZE);
+ }
rq_for_each_segment(bv, req, iter) {
dst = page_address(bv->bv_page) + bv->bv_offset;
seg_len = bv->bv_len;
+ if (cmd == DASD_ECKD_CCW_READ_TRACK)
+ memset(dst, 0, seg_len);
if (!len_to_track_end) {
ccw[-1].flags |= CCW_FLAG_CC;
ccw->cmd_code = cmd;
len_to_track_end -= seg_len;
idaws = idal_create_words(idaws, dst, seg_len);
}
-
+ for (sectors = 0; sectors < end_padding_sectors; sectors += 8)
+ idaws = idal_create_words(idaws, rawpadpage, PAGE_SIZE);
if (blk_noretry_request(req) ||
block->base->features & DASD_FEATURE_FAILFAST)
set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
kfree(dasd_reserve_req);
return -ENOMEM;
}
+ rawpadpage = (void *)__get_free_page(GFP_KERNEL);
+ if (!rawpadpage) {
+ kfree(path_verification_worker);
+ kfree(dasd_reserve_req);
+ return -ENOMEM;
+ }
ret = ccw_driver_register(&dasd_eckd_driver);
if (!ret)
wait_for_device_probe();
else {
kfree(path_verification_worker);
kfree(dasd_reserve_req);
+ free_page((unsigned long)rawpadpage);
}
return ret;
}
ccw_driver_unregister(&dasd_eckd_driver);
kfree(path_verification_worker);
kfree(dasd_reserve_req);
+ free_page((unsigned long)rawpadpage);
}
module_init(dasd_eckd_init);
struct dasd_ccw_req *dasd_default_erp_postaction(struct dasd_ccw_req *cqr)
{
int success;
+ unsigned long long startclk, stopclk;
+ struct dasd_device *startdev;
BUG_ON(cqr->refers == NULL || cqr->function == NULL);
success = cqr->status == DASD_CQR_DONE;
+ startclk = cqr->startclk;
+ stopclk = cqr->stopclk;
+ startdev = cqr->startdev;
/* free all ERPs - but NOT the original cqr */
while (cqr->refers != NULL) {
}
/* set corresponding status to original cqr */
+ cqr->startclk = startclk;
+ cqr->stopclk = stopclk;
+ cqr->startdev = startdev;
if (success)
cqr->status = DASD_CQR_DONE;
else {
device = cqr->startdev;
if (cqr->intrc == -ETIMEDOUT) {
- dev_err(&device->cdev->dev, "cqr %p timeout error", cqr);
+ dev_err(&device->cdev->dev,
+ "A timeout error occurred for cqr %p", cqr);
return;
}
if (cqr->intrc == -ENOLINK) {
- dev_err(&device->cdev->dev, "cqr %p transport error", cqr);
+ dev_err(&device->cdev->dev,
+ "A transport error occurred for cqr %p", cqr);
return;
}
/* dump sense data */
struct device *dev;
s390_adjust_jiffies();
- pr_warning("cpu capability changed.\n");
+ pr_info("CPU capability may have changed\n");
get_online_cpus();
for_each_online_cpu(cpu) {
dev = get_cpu_device(cpu);
}
EXPORT_SYMBOL(unregister_adapter_interrupt);
-void do_adapter_IO(u8 isc)
+static irqreturn_t do_airq_interrupt(int irq, void *dummy)
{
+ struct tpi_info *tpi_info;
struct airq_struct *airq;
struct hlist_head *head;
- head = &airq_lists[isc];
+ __this_cpu_write(s390_idle.nohz_delay, 1);
+ tpi_info = (struct tpi_info *) &get_irq_regs()->int_code;
+ head = &airq_lists[tpi_info->isc];
rcu_read_lock();
hlist_for_each_entry_rcu(airq, head, list)
if ((*airq->lsi_ptr & airq->lsi_mask) != 0)
airq->handler(airq);
rcu_read_unlock();
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction airq_interrupt = {
+ .name = "AIO",
+ .handler = do_airq_interrupt,
+};
+
+void __init init_airq_interrupts(void)
+{
+ irq_set_chip_and_handler(THIN_INTERRUPT,
+ &dummy_irq_chip, handle_percpu_irq);
+ setup_irq(THIN_INTERRUPT, &airq_interrupt);
+}
+
+/**
+ * airq_iv_create - create an interrupt vector
+ * @bits: number of bits in the interrupt vector
+ * @flags: allocation flags
+ *
+ * Returns a pointer to an interrupt vector structure
+ */
+struct airq_iv *airq_iv_create(unsigned long bits, unsigned long flags)
+{
+ struct airq_iv *iv;
+ unsigned long size;
+
+ iv = kzalloc(sizeof(*iv), GFP_KERNEL);
+ if (!iv)
+ goto out;
+ iv->bits = bits;
+ size = BITS_TO_LONGS(bits) * sizeof(unsigned long);
+ iv->vector = kzalloc(size, GFP_KERNEL);
+ if (!iv->vector)
+ goto out_free;
+ if (flags & AIRQ_IV_ALLOC) {
+ iv->avail = kmalloc(size, GFP_KERNEL);
+ if (!iv->avail)
+ goto out_free;
+ memset(iv->avail, 0xff, size);
+ iv->end = 0;
+ } else
+ iv->end = bits;
+ if (flags & AIRQ_IV_BITLOCK) {
+ iv->bitlock = kzalloc(size, GFP_KERNEL);
+ if (!iv->bitlock)
+ goto out_free;
+ }
+ if (flags & AIRQ_IV_PTR) {
+ size = bits * sizeof(unsigned long);
+ iv->ptr = kzalloc(size, GFP_KERNEL);
+ if (!iv->ptr)
+ goto out_free;
+ }
+ if (flags & AIRQ_IV_DATA) {
+ size = bits * sizeof(unsigned int);
+ iv->data = kzalloc(size, GFP_KERNEL);
+ if (!iv->data)
+ goto out_free;
+ }
+ spin_lock_init(&iv->lock);
+ return iv;
+
+out_free:
+ kfree(iv->ptr);
+ kfree(iv->bitlock);
+ kfree(iv->avail);
+ kfree(iv->vector);
+ kfree(iv);
+out:
+ return NULL;
+}
+EXPORT_SYMBOL(airq_iv_create);
+
+/**
+ * airq_iv_release - release an interrupt vector
+ * @iv: pointer to interrupt vector structure
+ */
+void airq_iv_release(struct airq_iv *iv)
+{
+ kfree(iv->data);
+ kfree(iv->ptr);
+ kfree(iv->bitlock);
+ kfree(iv->vector);
+ kfree(iv->avail);
+ kfree(iv);
+}
+EXPORT_SYMBOL(airq_iv_release);
+
+/**
+ * airq_iv_alloc_bit - allocate an irq bit from an interrupt vector
+ * @iv: pointer to an interrupt vector structure
+ *
+ * Returns the bit number of the allocated irq, or -1UL if no bit
+ * is available or the AIRQ_IV_ALLOC flag has not been specified
+ */
+unsigned long airq_iv_alloc_bit(struct airq_iv *iv)
+{
+ const unsigned long be_to_le = BITS_PER_LONG - 1;
+ unsigned long bit;
+
+ if (!iv->avail)
+ return -1UL;
+ spin_lock(&iv->lock);
+ bit = find_first_bit_left(iv->avail, iv->bits);
+ if (bit < iv->bits) {
+ clear_bit(bit ^ be_to_le, iv->avail);
+ if (bit >= iv->end)
+ iv->end = bit + 1;
+ } else
+ bit = -1UL;
+ spin_unlock(&iv->lock);
+ return bit;
+
+}
+EXPORT_SYMBOL(airq_iv_alloc_bit);
+
+/**
+ * airq_iv_free_bit - free an irq bit of an interrupt vector
+ * @iv: pointer to interrupt vector structure
+ * @bit: number of the irq bit to free
+ */
+void airq_iv_free_bit(struct airq_iv *iv, unsigned long bit)
+{
+ const unsigned long be_to_le = BITS_PER_LONG - 1;
+
+ if (!iv->avail)
+ return;
+ spin_lock(&iv->lock);
+ /* Clear (possibly left over) interrupt bit */
+ clear_bit(bit ^ be_to_le, iv->vector);
+ /* Make the bit position available again */
+ set_bit(bit ^ be_to_le, iv->avail);
+ if (bit == iv->end - 1) {
+ /* Find new end of bit-field */
+ while (--iv->end > 0)
+ if (!test_bit((iv->end - 1) ^ be_to_le, iv->avail))
+ break;
+ }
+ spin_unlock(&iv->lock);
+}
+EXPORT_SYMBOL(airq_iv_free_bit);
+
+/**
+ * airq_iv_scan - scan interrupt vector for non-zero bits
+ * @iv: pointer to interrupt vector structure
+ * @start: bit number to start the search
+ * @end: bit number to end the search
+ *
+ * Returns the bit number of the next non-zero interrupt bit, or
+ * -1UL if the scan completed without finding any more any non-zero bits.
+ */
+unsigned long airq_iv_scan(struct airq_iv *iv, unsigned long start,
+ unsigned long end)
+{
+ const unsigned long be_to_le = BITS_PER_LONG - 1;
+ unsigned long bit;
+
+ /* Find non-zero bit starting from 'ivs->next'. */
+ bit = find_next_bit_left(iv->vector, end, start);
+ if (bit >= end)
+ return -1UL;
+ /* Clear interrupt bit (find left uses big-endian bit numbers) */
+ clear_bit(bit ^ be_to_le, iv->vector);
+ return bit;
}
+EXPORT_SYMBOL(airq_iv_scan);
if (!try_module_get(gdrv->driver.owner))
return -EINVAL;
- ret = strict_strtoul(buf, 0, &value);
+ ret = kstrtoul(buf, 0, &value);
if (ret)
goto out;
}
/*
- * do_IRQ() handles all normal I/O device IRQ's (the special
- * SMP cross-CPU interrupts have their own specific
- * handlers).
- *
+ * do_cio_interrupt() handles all normal I/O device IRQ's
*/
-void __irq_entry do_IRQ(struct pt_regs *regs)
+static irqreturn_t do_cio_interrupt(int irq, void *dummy)
{
- struct tpi_info *tpi_info = (struct tpi_info *) ®s->int_code;
+ struct tpi_info *tpi_info;
struct subchannel *sch;
struct irb *irb;
- struct pt_regs *old_regs;
- old_regs = set_irq_regs(regs);
- irq_enter();
__this_cpu_write(s390_idle.nohz_delay, 1);
- if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator)
- /* Serve timer interrupts first. */
- clock_comparator_work();
-
- kstat_incr_irqs_this_cpu(IO_INTERRUPT, NULL);
+ tpi_info = (struct tpi_info *) &get_irq_regs()->int_code;
irb = (struct irb *) &S390_lowcore.irb;
- if (tpi_info->adapter_IO) {
- do_adapter_IO(tpi_info->isc);
- goto out;
- }
sch = (struct subchannel *)(unsigned long) tpi_info->intparm;
if (!sch) {
/* Clear pending interrupt condition. */
inc_irq_stat(IRQIO_CIO);
tsch(tpi_info->schid, irb);
- goto out;
+ return IRQ_HANDLED;
}
spin_lock(sch->lock);
/* Store interrupt response block to lowcore. */
} else
inc_irq_stat(IRQIO_CIO);
spin_unlock(sch->lock);
-out:
- irq_exit();
- set_irq_regs(old_regs);
+
+ return IRQ_HANDLED;
+}
+
+static struct irq_desc *irq_desc_io;
+
+static struct irqaction io_interrupt = {
+ .name = "IO",
+ .handler = do_cio_interrupt,
+};
+
+void __init init_cio_interrupts(void)
+{
+ irq_set_chip_and_handler(IO_INTERRUPT,
+ &dummy_irq_chip, handle_percpu_irq);
+ setup_irq(IO_INTERRUPT, &io_interrupt);
+ irq_desc_io = irq_to_desc(IO_INTERRUPT);
}
#ifdef CONFIG_CCW_CONSOLE
local_bh_disable();
irq_enter();
}
- kstat_incr_irqs_this_cpu(IO_INTERRUPT, NULL);
+ kstat_incr_irqs_this_cpu(IO_INTERRUPT, irq_desc_io);
if (sch->driver && sch->driver->irq)
sch->driver->irq(sch);
else
int cio_tm_start_key(struct subchannel *sch, struct tcw *tcw, u8 lpm, u8 key);
int cio_tm_intrg(struct subchannel *sch);
-void do_adapter_IO(u8 isc);
-void do_IRQ(struct pt_regs *);
-
/* Use with care. */
#ifdef CONFIG_CCW_CONSOLE
extern struct subchannel *cio_probe_console(void);
int ret;
unsigned long val;
- ret = strict_strtoul(buf, 16, &val);
+ ret = kstrtoul(buf, 16, &val);
if (ret)
return ret;
case -ENOMEM:
case -EIO:
/* These should abort looping */
+ spin_lock_irq(&slow_subchannel_lock);
idset_sch_del_subseq(slow_subchannel_set, schid);
+ spin_unlock_irq(&slow_subchannel_lock);
break;
default:
rc = 0;
int ret;
unsigned long val;
- ret = strict_strtoul(buf, 16, &val);
+ ret = kstrtoul(buf, 16, &val);
if (ret)
return ret;
mutex_lock(&css->mutex);
extern struct channel_subsystem *channel_subsystems[];
-void channel_subsystem_reinit(void);
-
/* Helper functions to build lists for the slow path. */
void css_schedule_eval(struct subchannel_id schid);
void css_schedule_eval_all(void);
ret = 0;
} else {
force = 0;
- ret = strict_strtoul(buf, 16, &i);
+ ret = kstrtoul(buf, 16, &i);
}
if (ret)
goto out;
goto out;
}
- rc = strict_strtoul(buf, 16, &i);
+ rc = kstrtoul(buf, 16, &i);
if (rc) {
rc = -EINVAL;
goto out;
if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_INUSE)
zfcp_erp_action_dismiss(&port->erp_action);
- else
- shost_for_each_device(sdev, port->adapter->scsi_host)
+ else {
+ spin_lock(port->adapter->scsi_host->host_lock);
+ __shost_for_each_device(sdev, port->adapter->scsi_host)
if (sdev_to_zfcp(sdev)->port == port)
zfcp_erp_action_dismiss_lun(sdev);
+ spin_unlock(port->adapter->scsi_host->host_lock);
+ }
}
static void zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *adapter)
{
struct scsi_device *sdev;
- shost_for_each_device(sdev, port->adapter->scsi_host)
+ spin_lock(port->adapter->scsi_host->host_lock);
+ __shost_for_each_device(sdev, port->adapter->scsi_host)
if (sdev_to_zfcp(sdev)->port == port)
_zfcp_erp_lun_reopen(sdev, clear, id, 0);
+ spin_unlock(port->adapter->scsi_host->host_lock);
}
static void zfcp_erp_strategy_followup_failed(struct zfcp_erp_action *act)
atomic_set_mask(common_mask, &port->status);
read_unlock_irqrestore(&adapter->port_list_lock, flags);
- shost_for_each_device(sdev, adapter->scsi_host)
+ spin_lock_irqsave(adapter->scsi_host->host_lock, flags);
+ __shost_for_each_device(sdev, adapter->scsi_host)
atomic_set_mask(common_mask, &sdev_to_zfcp(sdev)->status);
+ spin_unlock_irqrestore(adapter->scsi_host->host_lock, flags);
}
/**
}
read_unlock_irqrestore(&adapter->port_list_lock, flags);
- shost_for_each_device(sdev, adapter->scsi_host) {
+ spin_lock_irqsave(adapter->scsi_host->host_lock, flags);
+ __shost_for_each_device(sdev, adapter->scsi_host) {
atomic_clear_mask(common_mask, &sdev_to_zfcp(sdev)->status);
if (clear_counter)
atomic_set(&sdev_to_zfcp(sdev)->erp_counter, 0);
}
+ spin_unlock_irqrestore(adapter->scsi_host->host_lock, flags);
}
/**
{
struct scsi_device *sdev;
u32 common_mask = mask & ZFCP_COMMON_FLAGS;
+ unsigned long flags;
atomic_set_mask(mask, &port->status);
if (!common_mask)
return;
- shost_for_each_device(sdev, port->adapter->scsi_host)
+ spin_lock_irqsave(port->adapter->scsi_host->host_lock, flags);
+ __shost_for_each_device(sdev, port->adapter->scsi_host)
if (sdev_to_zfcp(sdev)->port == port)
atomic_set_mask(common_mask,
&sdev_to_zfcp(sdev)->status);
+ spin_unlock_irqrestore(port->adapter->scsi_host->host_lock, flags);
}
/**
struct scsi_device *sdev;
u32 common_mask = mask & ZFCP_COMMON_FLAGS;
u32 clear_counter = mask & ZFCP_STATUS_COMMON_ERP_FAILED;
+ unsigned long flags;
atomic_clear_mask(mask, &port->status);
if (clear_counter)
atomic_set(&port->erp_counter, 0);
- shost_for_each_device(sdev, port->adapter->scsi_host)
+ spin_lock_irqsave(port->adapter->scsi_host->host_lock, flags);
+ __shost_for_each_device(sdev, port->adapter->scsi_host)
if (sdev_to_zfcp(sdev)->port == port) {
atomic_clear_mask(common_mask,
&sdev_to_zfcp(sdev)->status);
if (clear_counter)
atomic_set(&sdev_to_zfcp(sdev)->erp_counter, 0);
}
+ spin_unlock_irqrestore(port->adapter->scsi_host->host_lock, flags);
}
/**
static int zfcp_qdio_sbal_check(struct zfcp_qdio *qdio)
{
- spin_lock_irq(&qdio->req_q_lock);
if (atomic_read(&qdio->req_q_free) ||
!(atomic_read(&qdio->adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP))
return 1;
- spin_unlock_irq(&qdio->req_q_lock);
return 0;
}
{
long ret;
- spin_unlock_irq(&qdio->req_q_lock);
- ret = wait_event_interruptible_timeout(qdio->req_q_wq,
- zfcp_qdio_sbal_check(qdio), 5 * HZ);
+ ret = wait_event_interruptible_lock_irq_timeout(qdio->req_q_wq,
+ zfcp_qdio_sbal_check(qdio), qdio->req_q_lock, 5 * HZ);
if (!(atomic_read(&qdio->adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP))
return -EIO;
zfcp_erp_adapter_reopen(qdio->adapter, 0, "qdsbg_1");
}
- spin_lock_irq(&qdio->req_q_lock);
return -EIO;
}
static ZFCP_DEV_ATTR(_feat, _name, S_IRUGO, \
zfcp_sysfs_##_feat##_##_name##_show, NULL);
+#define ZFCP_DEFINE_ATTR_CONST(_feat, _name, _format, _value) \
+static ssize_t zfcp_sysfs_##_feat##_##_name##_show(struct device *dev, \
+ struct device_attribute *at,\
+ char *buf) \
+{ \
+ return sprintf(buf, _format, _value); \
+} \
+static ZFCP_DEV_ATTR(_feat, _name, S_IRUGO, \
+ zfcp_sysfs_##_feat##_##_name##_show, NULL);
+
#define ZFCP_DEFINE_A_ATTR(_name, _format, _value) \
static ssize_t zfcp_sysfs_adapter_##_name##_show(struct device *dev, \
struct device_attribute *at,\
ZFCP_DEFINE_ATTR(zfcp_unit, unit, access_denied, "%d\n",
(zfcp_unit_sdev_status(unit) &
ZFCP_STATUS_COMMON_ACCESS_DENIED) != 0);
+ZFCP_DEFINE_ATTR_CONST(unit, access_shared, "%d\n", 0);
+ZFCP_DEFINE_ATTR_CONST(unit, access_readonly, "%d\n", 0);
static ssize_t zfcp_sysfs_port_failed_show(struct device *dev,
struct device_attribute *attr,
&dev_attr_unit_in_recovery.attr,
&dev_attr_unit_status.attr,
&dev_attr_unit_access_denied.attr,
+ &dev_attr_unit_access_shared.attr,
+ &dev_attr_unit_access_readonly.attr,
NULL
};
static struct attribute_group zfcp_unit_attr_group = {
tristate "Emulex LightPulse Fibre Channel Support"
depends on PCI && SCSI
select SCSI_FC_ATTRS
- select GENERIC_CSUM
select CRC_T10DIF
help
This lpfc driver supports the Emulex LightPulse
pm8001_ha->main_cfg_tbl.pm8001_tbl.fatal_err_interrupt = 0x01;
for (i = 0; i < PM8001_MAX_INB_NUM; i++) {
pm8001_ha->inbnd_q_tbl[i].element_pri_size_cnt =
- PM8001_MPI_QUEUE | (64 << 16) | (0x00<<30);
+ PM8001_MPI_QUEUE | (pm8001_ha->iomb_size << 16) | (0x00<<30);
pm8001_ha->inbnd_q_tbl[i].upper_base_addr =
pm8001_ha->memoryMap.region[IB + i].phys_addr_hi;
pm8001_ha->inbnd_q_tbl[i].lower_base_addr =
}
for (i = 0; i < PM8001_MAX_OUTB_NUM; i++) {
pm8001_ha->outbnd_q_tbl[i].element_size_cnt =
- PM8001_MPI_QUEUE | (64 << 16) | (0x01<<30);
+ PM8001_MPI_QUEUE | (pm8001_ha->iomb_size << 16) | (0x01<<30);
pm8001_ha->outbnd_q_tbl[i].upper_base_addr =
pm8001_ha->memoryMap.region[OB + i].phys_addr_hi;
pm8001_ha->outbnd_q_tbl[i].lower_base_addr =
for (i = 0; i < PM8001_MAX_SPCV_INB_NUM; i++) {
pm8001_ha->inbnd_q_tbl[i].element_pri_size_cnt =
- PM8001_MPI_QUEUE | (64 << 16) | (0x00<<30);
+ PM8001_MPI_QUEUE | (pm8001_ha->iomb_size << 16) | (0x00<<30);
pm8001_ha->inbnd_q_tbl[i].upper_base_addr =
pm8001_ha->memoryMap.region[IB + i].phys_addr_hi;
pm8001_ha->inbnd_q_tbl[i].lower_base_addr =
}
for (i = 0; i < PM8001_MAX_SPCV_OUTB_NUM; i++) {
pm8001_ha->outbnd_q_tbl[i].element_size_cnt =
- PM8001_MPI_QUEUE | (64 << 16) | (0x01<<30);
+ PM8001_MPI_QUEUE | (pm8001_ha->iomb_size << 16) | (0x01<<30);
pm8001_ha->outbnd_q_tbl[i].upper_base_addr =
pm8001_ha->memoryMap.region[OB + i].phys_addr_hi;
pm8001_ha->outbnd_q_tbl[i].lower_base_addr =
config SPI_ATMEL
tristate "Atmel SPI Controller"
- depends on (ARCH_AT91 || AVR32)
+ depends on (ARCH_AT91 || AVR32 || COMPILE_TEST)
help
This selects a driver for the Atmel SPI Controller, present on
many AT32 (AVR32) and AT91 (ARM) chips.
config SPI_BCM2835
tristate "BCM2835 SPI controller"
- depends on ARCH_BCM2835
+ depends on ARCH_BCM2835 || COMPILE_TEST
help
This selects a driver for the Broadcom BCM2835 SPI master.
config SPI_BFIN5XX
tristate "SPI controller driver for ADI Blackfin5xx"
- depends on BLACKFIN
+ depends on BLACKFIN && !BF60x
help
This is the SPI controller master driver for Blackfin 5xx processor.
+config SPI_BFIN_V3
+ tristate "SPI controller v3 for Blackfin"
+ depends on BF60x
+ help
+ This is the SPI controller v3 master driver
+ found on Blackfin 60x processor.
+
config SPI_BFIN_SPORT
tristate "SPI bus via Blackfin SPORT"
depends on BLACKFIN
config SPI_DAVINCI
tristate "Texas Instruments DaVinci/DA8x/OMAP-L/AM1x SoC SPI controller"
- depends on ARCH_DAVINCI
+ depends on ARCH_DAVINCI || ARCH_KEYSTONE
select SPI_BITBANG
select TI_EDMA
help
SPI master controller for DaVinci/DA8x/OMAP-L/AM1x SPI modules.
+config SPI_EFM32
+ tristate "EFM32 SPI controller"
+ depends on OF && ARM && (ARCH_EFM32 || COMPILE_TEST)
+ select SPI_BITBANG
+ help
+ Driver for the spi controller found on Energy Micro's EFM32 SoCs.
+
config SPI_EP93XX
tristate "Cirrus Logic EP93xx SPI controller"
- depends on ARCH_EP93XX
+ depends on ARCH_EP93XX || COMPILE_TEST
help
This enables using the Cirrus EP93xx SPI controller in master
mode.
config SPI_IMX
tristate "Freescale i.MX SPI controllers"
- depends on ARCH_MXC
+ depends on ARCH_MXC || COMPILE_TEST
select SPI_BITBANG
default m if IMX_HAVE_PLATFORM_SPI_IMX
help
This also enables using the Aeroflex Gaisler GRLIB SPI controller in
master mode.
+config SPI_FSL_DSPI
+ tristate "Freescale DSPI controller"
+ select SPI_BITBANG
+ help
+ This enables support for the Freescale DSPI controller in master
+ mode. VF610 platform uses the controller.
+
config SPI_FSL_ESPI
bool "Freescale eSPI controller"
depends on FSL_SOC
config SPI_OMAP24XX
tristate "McSPI driver for OMAP"
- depends on ARCH_OMAP2PLUS
+ depends on ARCH_OMAP2PLUS || COMPILE_TEST
help
SPI master controller for OMAP24XX and later Multichannel SPI
(McSPI) modules.
+config SPI_TI_QSPI
+ tristate "DRA7xxx QSPI controller support"
+ depends on ARCH_OMAP2PLUS || COMPILE_TEST
+ help
+ QSPI master controller for DRA7xxx used for flash devices.
+ This device supports single, dual and quad read support, while
+ it only supports single write mode.
+
config SPI_OMAP_100K
tristate "OMAP SPI 100K"
- depends on ARCH_OMAP850 || ARCH_OMAP730
+ depends on ARCH_OMAP850 || ARCH_OMAP730 || COMPILE_TEST
help
OMAP SPI 100K master controller for omap7xx boards.
config SPI_ORION
tristate "Orion SPI master"
- depends on PLAT_ORION
+ depends on PLAT_ORION || COMPILE_TEST
help
This enables using the SPI master controller on the Orion chips.
config SPI_RSPI
tristate "Renesas RSPI controller"
- depends on SUPERH
+ depends on SUPERH && SH_DMAE_BASE
help
SPI driver for Renesas RSPI blocks.
config SPI_SH
tristate "SuperH SPI controller"
- depends on SUPERH
+ depends on SUPERH || COMPILE_TEST
help
SPI driver for SuperH SPI blocks.
config SPI_SH_HSPI
tristate "SuperH HSPI controller"
- depends on ARCH_SHMOBILE
+ depends on ARCH_SHMOBILE || COMPILE_TEST
help
SPI driver for SuperH HSPI blocks.
config SPI_SIRF
tristate "CSR SiRFprimaII SPI controller"
- depends on ARCH_SIRF
+ depends on SIRF_DMA
select SPI_BITBANG
help
SPI driver for CSR SiRFprimaII SoCs
config SPI_TEGRA114
tristate "NVIDIA Tegra114 SPI Controller"
- depends on ARCH_TEGRA && TEGRA20_APB_DMA
+ depends on (ARCH_TEGRA && TEGRA20_APB_DMA) || COMPILE_TEST
help
SPI driver for NVIDIA Tegra114 SPI Controller interface. This controller
is different than the older SoCs SPI controller and also register interface
config SPI_TEGRA20_SFLASH
tristate "Nvidia Tegra20 Serial flash Controller"
- depends on ARCH_TEGRA
+ depends on ARCH_TEGRA || COMPILE_TEST
help
SPI driver for Nvidia Tegra20 Serial flash Controller interface.
The main usecase of this controller is to use spi flash as boot
config SPI_TEGRA20_SLINK
tristate "Nvidia Tegra20/Tegra30 SLINK Controller"
- depends on ARCH_TEGRA && TEGRA20_APB_DMA
+ depends on (ARCH_TEGRA && TEGRA20_APB_DMA) || COMPILE_TEST
help
SPI driver for Nvidia Tegra20/Tegra30 SLINK Controller interface.
config SPI_TXX9
tristate "Toshiba TXx9 SPI controller"
- depends on GPIOLIB && CPU_TX49XX
+ depends on GPIOLIB && (CPU_TX49XX || COMPILE_TEST)
help
SPI driver for Toshiba TXx9 MIPS SoCs
obj-$(CONFIG_SPI_BCM2835) += spi-bcm2835.o
obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o
obj-$(CONFIG_SPI_BFIN5XX) += spi-bfin5xx.o
+obj-$(CONFIG_SPI_BFIN_V3) += spi-bfin-v3.o
obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o
obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o
obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o
obj-$(CONFIG_SPI_DW_MMIO) += spi-dw-mmio.o
obj-$(CONFIG_SPI_DW_PCI) += spi-dw-midpci.o
spi-dw-midpci-objs := spi-dw-pci.o spi-dw-mid.o
+obj-$(CONFIG_SPI_EFM32) += spi-efm32.o
obj-$(CONFIG_SPI_EP93XX) += spi-ep93xx.o
obj-$(CONFIG_SPI_FALCON) += spi-falcon.o
obj-$(CONFIG_SPI_FSL_CPM) += spi-fsl-cpm.o
+obj-$(CONFIG_SPI_FSL_DSPI) += spi-fsl-dspi.o
obj-$(CONFIG_SPI_FSL_LIB) += spi-fsl-lib.o
obj-$(CONFIG_SPI_FSL_ESPI) += spi-fsl-espi.o
obj-$(CONFIG_SPI_FSL_SPI) += spi-fsl-spi.o
obj-$(CONFIG_SPI_OMAP_UWIRE) += spi-omap-uwire.o
obj-$(CONFIG_SPI_OMAP_100K) += spi-omap-100k.o
obj-$(CONFIG_SPI_OMAP24XX) += spi-omap2-mcspi.o
+obj-$(CONFIG_SPI_TI_QSPI) += spi-ti-qspi.o
obj-$(CONFIG_SPI_ORION) += spi-orion.o
obj-$(CONFIG_SPI_PL022) += spi-pl022.o
obj-$(CONFIG_SPI_PPC4xx) += spi-ppc4xx.o
}
}
-static int altera_spi_setupxfer(struct spi_device *spi, struct spi_transfer *t)
-{
- return 0;
-}
-
-static int altera_spi_setup(struct spi_device *spi)
-{
- return 0;
-}
-
static inline unsigned int hw_txbyte(struct altera_spi *hw, int count)
{
if (hw->tx) {
hw->tx = t->tx_buf;
hw->rx = t->rx_buf;
hw->count = 0;
- hw->bytes_per_word = t->bits_per_word / 8;
+ hw->bytes_per_word = DIV_ROUND_UP(t->bits_per_word, 8);
hw->len = t->len / hw->bytes_per_word;
if (hw->irq >= 0) {
hw->imr &= ~ALTERA_SPI_CONTROL_IRRDY_MSK;
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
} else {
- /* send the first byte */
- writel(hw_txbyte(hw, 0), hw->base + ALTERA_SPI_TXDATA);
-
- while (1) {
+ while (hw->count < hw->len) {
unsigned int rxd;
+ writel(hw_txbyte(hw, hw->count),
+ hw->base + ALTERA_SPI_TXDATA);
+
while (!(readl(hw->base + ALTERA_SPI_STATUS) &
ALTERA_SPI_STATUS_RRDY_MSK))
cpu_relax();
}
hw->count++;
-
- if (hw->count < hw->len)
- writel(hw_txbyte(hw, hw->count),
- hw->base + ALTERA_SPI_TXDATA);
- else
- break;
}
-
}
return hw->count * hw->bytes_per_word;
static int altera_spi_probe(struct platform_device *pdev)
{
- struct altera_spi_platform_data *platp = pdev->dev.platform_data;
+ struct altera_spi_platform_data *platp = dev_get_platdata(&pdev->dev);
struct altera_spi *hw;
struct spi_master *master;
struct resource *res;
master->bus_num = pdev->id;
master->num_chipselect = 16;
master->mode_bits = SPI_CS_HIGH;
- master->setup = altera_spi_setup;
hw = spi_master_get_devdata(master);
platform_set_drvdata(pdev, hw);
hw->bitbang.master = spi_master_get(master);
if (!hw->bitbang.master)
return err;
- hw->bitbang.setup_transfer = altera_spi_setupxfer;
hw->bitbang.chipselect = altera_spi_chipsel;
hw->bitbang.txrx_bufs = altera_spi_txrx;
/* find and map our resources */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- goto exit_busy;
- if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
- pdev->name))
- goto exit_busy;
- hw->base = devm_ioremap_nocache(&pdev->dev, res->start,
- resource_size(res));
- if (!hw->base)
- goto exit_busy;
+ hw->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(hw->base)) {
+ err = PTR_ERR(hw->base);
+ goto exit;
+ }
/* program defaults into the registers */
hw->imr = 0; /* disable spi interrupts */
writel(hw->imr, hw->base + ALTERA_SPI_CONTROL);
dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq);
return 0;
-
-exit_busy:
- err = -EBUSY;
exit:
spi_master_put(master);
return err;
sp = spi_master_get_devdata(master);
platform_set_drvdata(pdev, sp);
- pdata = pdev->dev.platform_data;
+ pdata = dev_get_platdata(&pdev->dev);
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
master->setup = ath79_spi_setup;
gpio_set_value(asd->npcs_pin, !active);
}
-static void atmel_spi_lock(struct atmel_spi *as)
+static void atmel_spi_lock(struct atmel_spi *as) __acquires(&as->lock)
{
spin_lock_irqsave(&as->lock, as->flags);
}
-static void atmel_spi_unlock(struct atmel_spi *as)
+static void atmel_spi_unlock(struct atmel_spi *as) __releases(&as->lock)
{
spin_unlock_irqrestore(&as->lock, as->flags);
}
goto err_dma;
dev_dbg(master->dev.parent,
- " start dma xfer %p: len %u tx %p/%08x rx %p/%08x\n",
- xfer, xfer->len, xfer->tx_buf, xfer->tx_dma,
- xfer->rx_buf, xfer->rx_dma);
+ " start dma xfer %p: len %u tx %p/%08llx rx %p/%08llx\n",
+ xfer, xfer->len, xfer->tx_buf, (unsigned long long)xfer->tx_dma,
+ xfer->rx_buf, (unsigned long long)xfer->rx_dma);
/* Enable relevant interrupts */
spi_writel(as, IER, SPI_BIT(OVRES));
spi_writel(as, TCR, len);
dev_dbg(&msg->spi->dev,
- " start xfer %p: len %u tx %p/%08x rx %p/%08x\n",
- xfer, xfer->len, xfer->tx_buf, xfer->tx_dma,
- xfer->rx_buf, xfer->rx_dma);
+ " start xfer %p: len %u tx %p/%08llx rx %p/%08llx\n",
+ xfer, xfer->len, xfer->tx_buf,
+ (unsigned long long)xfer->tx_dma, xfer->rx_buf,
+ (unsigned long long)xfer->rx_dma);
} else {
xfer = as->next_transfer;
remaining = as->next_remaining_bytes;
spi_writel(as, TNCR, len);
dev_dbg(&msg->spi->dev,
- " next xfer %p: len %u tx %p/%08x rx %p/%08x\n",
- xfer, xfer->len, xfer->tx_buf, xfer->tx_dma,
- xfer->rx_buf, xfer->rx_dma);
+ " next xfer %p: len %u tx %p/%08llx rx %p/%08llx\n",
+ xfer, xfer->len, xfer->tx_buf,
+ (unsigned long long)xfer->tx_dma, xfer->rx_buf,
+ (unsigned long long)xfer->rx_dma);
ieval = SPI_BIT(ENDRX) | SPI_BIT(OVRES);
} else {
spi_writel(as, RNCR, 0);
goto out_unmap_regs;
/* Initialize the hardware */
- clk_enable(clk);
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ goto out_unmap_regs;
spi_writel(as, CR, SPI_BIT(SWRST));
spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */
if (as->caps.has_wdrbt) {
spi_writel(as, CR, SPI_BIT(SWRST));
spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */
- clk_disable(clk);
+ clk_disable_unprepare(clk);
free_irq(irq, master);
out_unmap_regs:
iounmap(as->regs);
dma_free_coherent(&pdev->dev, BUFFER_SIZE, as->buffer,
as->buffer_dma);
- clk_disable(as->clk);
+ clk_disable_unprepare(as->clk);
clk_put(as->clk);
free_irq(as->irq, master);
iounmap(as->regs);
struct spi_master *master = platform_get_drvdata(pdev);
struct atmel_spi *as = spi_master_get_devdata(master);
- clk_disable(as->clk);
+ clk_disable_unprepare(as->clk);
return 0;
}
struct spi_master *master = platform_get_drvdata(pdev);
struct atmel_spi *as = spi_master_get_devdata(master);
- clk_enable(as->clk);
+ return clk_prepare_enable(as->clk);
return 0;
}
hw = spi_master_get_devdata(master);
hw->master = spi_master_get(master);
- hw->pdata = pdev->dev.platform_data;
+ hw->pdata = dev_get_platdata(&pdev->dev);
hw->dev = &pdev->dev;
if (hw->pdata == NULL) {
platform_set_drvdata(pdev, master);
master->mode_bits = BCM2835_SPI_MODE_BITS;
- master->bits_per_word_mask = BIT(8 - 1);
+ master->bits_per_word_mask = SPI_BPW_MASK(8);
master->bus_num = -1;
master->num_chipselect = 3;
master->transfer_one_message = bcm2835_spi_transfer_one;
init_completion(&bs->done);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "could not get memory resource\n");
- err = -ENODEV;
- goto out_master_put;
- }
-
bs->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(bs->regs)) {
err = PTR_ERR(bs->regs);
static int bcm2835_spi_remove(struct platform_device *pdev)
{
- struct spi_master *master = platform_get_drvdata(pdev);
+ struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
struct bcm2835_spi *bs = spi_master_get_devdata(master);
free_irq(bs->irq, master);
return 0;
}
-static int bcm63xx_spi_prepare_transfer(struct spi_master *master)
-{
- struct bcm63xx_spi *bs = spi_master_get_devdata(master);
-
- pm_runtime_get_sync(&bs->pdev->dev);
-
- return 0;
-}
-
-static int bcm63xx_spi_unprepare_transfer(struct spi_master *master)
-{
- struct bcm63xx_spi *bs = spi_master_get_devdata(master);
-
- pm_runtime_put(&bs->pdev->dev);
-
- return 0;
-}
-
static int bcm63xx_spi_transfer_one(struct spi_master *master,
struct spi_message *m)
{
{
struct resource *r;
struct device *dev = &pdev->dev;
- struct bcm63xx_spi_pdata *pdata = pdev->dev.platform_data;
+ struct bcm63xx_spi_pdata *pdata = dev_get_platdata(&pdev->dev);
int irq;
struct spi_master *master;
struct clk *clk;
struct bcm63xx_spi *bs;
int ret;
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!r) {
- dev_err(dev, "no iomem\n");
- ret = -ENXIO;
- goto out;
- }
-
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(dev, "no irq\n");
platform_set_drvdata(pdev, master);
bs->pdev = pdev;
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
bs->regs = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(bs->regs)) {
ret = PTR_ERR(bs->regs);
master->bus_num = pdata->bus_num;
master->num_chipselect = pdata->num_chipselect;
- master->prepare_transfer_hardware = bcm63xx_spi_prepare_transfer;
- master->unprepare_transfer_hardware = bcm63xx_spi_unprepare_transfer;
master->transfer_one_message = bcm63xx_spi_transfer_one;
master->mode_bits = MODEBITS;
master->bits_per_word_mask = SPI_BPW_MASK(8);
+ master->auto_runtime_pm = true;
bs->msg_type_shift = pdata->msg_type_shift;
bs->msg_ctl_width = pdata->msg_ctl_width;
bs->tx_io = (u8 *)(bs->regs + bcm63xx_spireg(SPI_MSG_DATA));
#ifdef CONFIG_PM
static int bcm63xx_spi_suspend(struct device *dev)
{
- struct spi_master *master =
- platform_get_drvdata(to_platform_device(dev));
+ struct spi_master *master = dev_get_drvdata(dev);
struct bcm63xx_spi *bs = spi_master_get_devdata(master);
spi_master_suspend(master);
static int bcm63xx_spi_resume(struct device *dev)
{
- struct spi_master *master =
- platform_get_drvdata(to_platform_device(dev));
+ struct spi_master *master = dev_get_drvdata(dev);
struct bcm63xx_spi *bs = spi_master_get_devdata(master);
clk_prepare_enable(bs->clk);
struct bfin_sport_spi_master_data *drv_data;
int status;
- platform_info = dev->platform_data;
+ platform_info = dev_get_platdata(dev);
/* Allocate master with space for drv_data */
master = spi_alloc_master(dev, sizeof(*master) + 16);
--- /dev/null
+/*
+ * Analog Devices SPI3 controller driver
+ *
+ * Copyright (c) 2013 Analog Devices 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.
+ *
+ * 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/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+
+#include <asm/bfin_spi3.h>
+#include <asm/cacheflush.h>
+#include <asm/dma.h>
+#include <asm/portmux.h>
+
+enum bfin_spi_state {
+ START_STATE,
+ RUNNING_STATE,
+ DONE_STATE,
+ ERROR_STATE
+};
+
+struct bfin_spi_master;
+
+struct bfin_spi_transfer_ops {
+ void (*write) (struct bfin_spi_master *);
+ void (*read) (struct bfin_spi_master *);
+ void (*duplex) (struct bfin_spi_master *);
+};
+
+/* runtime info for spi master */
+struct bfin_spi_master {
+ /* SPI framework hookup */
+ struct spi_master *master;
+
+ /* Regs base of SPI controller */
+ struct bfin_spi_regs __iomem *regs;
+
+ /* Pin request list */
+ u16 *pin_req;
+
+ /* Message Transfer pump */
+ struct tasklet_struct pump_transfers;
+
+ /* Current message transfer state info */
+ struct spi_message *cur_msg;
+ struct spi_transfer *cur_transfer;
+ struct bfin_spi_device *cur_chip;
+ unsigned transfer_len;
+
+ /* transfer buffer */
+ void *tx;
+ void *tx_end;
+ void *rx;
+ void *rx_end;
+
+ /* dma info */
+ unsigned int tx_dma;
+ unsigned int rx_dma;
+ dma_addr_t tx_dma_addr;
+ dma_addr_t rx_dma_addr;
+ unsigned long dummy_buffer; /* used in unidirectional transfer */
+ unsigned long tx_dma_size;
+ unsigned long rx_dma_size;
+ int tx_num;
+ int rx_num;
+
+ /* store register value for suspend/resume */
+ u32 control;
+ u32 ssel;
+
+ unsigned long sclk;
+ enum bfin_spi_state state;
+
+ const struct bfin_spi_transfer_ops *ops;
+};
+
+struct bfin_spi_device {
+ u32 control;
+ u32 clock;
+ u32 ssel;
+
+ u8 cs;
+ u16 cs_chg_udelay; /* Some devices require > 255usec delay */
+ u32 cs_gpio;
+ u32 tx_dummy_val; /* tx value for rx only transfer */
+ bool enable_dma;
+ const struct bfin_spi_transfer_ops *ops;
+};
+
+static void bfin_spi_enable(struct bfin_spi_master *drv_data)
+{
+ bfin_write_or(&drv_data->regs->control, SPI_CTL_EN);
+}
+
+static void bfin_spi_disable(struct bfin_spi_master *drv_data)
+{
+ bfin_write_and(&drv_data->regs->control, ~SPI_CTL_EN);
+}
+
+/* Caculate the SPI_CLOCK register value based on input HZ */
+static u32 hz_to_spi_clock(u32 sclk, u32 speed_hz)
+{
+ u32 spi_clock = sclk / speed_hz;
+
+ if (spi_clock)
+ spi_clock--;
+ return spi_clock;
+}
+
+static int bfin_spi_flush(struct bfin_spi_master *drv_data)
+{
+ unsigned long limit = loops_per_jiffy << 1;
+
+ /* wait for stop and clear stat */
+ while (!(bfin_read(&drv_data->regs->status) & SPI_STAT_SPIF) && --limit)
+ cpu_relax();
+
+ bfin_write(&drv_data->regs->status, 0xFFFFFFFF);
+
+ return limit;
+}
+
+/* Chip select operation functions for cs_change flag */
+static void bfin_spi_cs_active(struct bfin_spi_master *drv_data, struct bfin_spi_device *chip)
+{
+ if (likely(chip->cs < MAX_CTRL_CS))
+ bfin_write_and(&drv_data->regs->ssel, ~chip->ssel);
+ else
+ gpio_set_value(chip->cs_gpio, 0);
+}
+
+static void bfin_spi_cs_deactive(struct bfin_spi_master *drv_data,
+ struct bfin_spi_device *chip)
+{
+ if (likely(chip->cs < MAX_CTRL_CS))
+ bfin_write_or(&drv_data->regs->ssel, chip->ssel);
+ else
+ gpio_set_value(chip->cs_gpio, 1);
+
+ /* Move delay here for consistency */
+ if (chip->cs_chg_udelay)
+ udelay(chip->cs_chg_udelay);
+}
+
+/* enable or disable the pin muxed by GPIO and SPI CS to work as SPI CS */
+static inline void bfin_spi_cs_enable(struct bfin_spi_master *drv_data,
+ struct bfin_spi_device *chip)
+{
+ if (chip->cs < MAX_CTRL_CS)
+ bfin_write_or(&drv_data->regs->ssel, chip->ssel >> 8);
+}
+
+static inline void bfin_spi_cs_disable(struct bfin_spi_master *drv_data,
+ struct bfin_spi_device *chip)
+{
+ if (chip->cs < MAX_CTRL_CS)
+ bfin_write_and(&drv_data->regs->ssel, ~(chip->ssel >> 8));
+}
+
+/* stop controller and re-config current chip*/
+static void bfin_spi_restore_state(struct bfin_spi_master *drv_data)
+{
+ struct bfin_spi_device *chip = drv_data->cur_chip;
+
+ /* Clear status and disable clock */
+ bfin_write(&drv_data->regs->status, 0xFFFFFFFF);
+ bfin_write(&drv_data->regs->rx_control, 0x0);
+ bfin_write(&drv_data->regs->tx_control, 0x0);
+ bfin_spi_disable(drv_data);
+
+ SSYNC();
+
+ /* Load the registers */
+ bfin_write(&drv_data->regs->control, chip->control);
+ bfin_write(&drv_data->regs->clock, chip->clock);
+
+ bfin_spi_enable(drv_data);
+ drv_data->tx_num = drv_data->rx_num = 0;
+ /* we always choose tx transfer initiate */
+ bfin_write(&drv_data->regs->rx_control, SPI_RXCTL_REN);
+ bfin_write(&drv_data->regs->tx_control,
+ SPI_TXCTL_TEN | SPI_TXCTL_TTI);
+ bfin_spi_cs_active(drv_data, chip);
+}
+
+/* discard invalid rx data and empty rfifo */
+static inline void dummy_read(struct bfin_spi_master *drv_data)
+{
+ while (!(bfin_read(&drv_data->regs->status) & SPI_STAT_RFE))
+ bfin_read(&drv_data->regs->rfifo);
+}
+
+static void bfin_spi_u8_write(struct bfin_spi_master *drv_data)
+{
+ dummy_read(drv_data);
+ while (drv_data->tx < drv_data->tx_end) {
+ bfin_write(&drv_data->regs->tfifo, (*(u8 *)(drv_data->tx++)));
+ while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE)
+ cpu_relax();
+ bfin_read(&drv_data->regs->rfifo);
+ }
+}
+
+static void bfin_spi_u8_read(struct bfin_spi_master *drv_data)
+{
+ u32 tx_val = drv_data->cur_chip->tx_dummy_val;
+
+ dummy_read(drv_data);
+ while (drv_data->rx < drv_data->rx_end) {
+ bfin_write(&drv_data->regs->tfifo, tx_val);
+ while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE)
+ cpu_relax();
+ *(u8 *)(drv_data->rx++) = bfin_read(&drv_data->regs->rfifo);
+ }
+}
+
+static void bfin_spi_u8_duplex(struct bfin_spi_master *drv_data)
+{
+ dummy_read(drv_data);
+ while (drv_data->rx < drv_data->rx_end) {
+ bfin_write(&drv_data->regs->tfifo, (*(u8 *)(drv_data->tx++)));
+ while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE)
+ cpu_relax();
+ *(u8 *)(drv_data->rx++) = bfin_read(&drv_data->regs->rfifo);
+ }
+}
+
+static const struct bfin_spi_transfer_ops bfin_bfin_spi_transfer_ops_u8 = {
+ .write = bfin_spi_u8_write,
+ .read = bfin_spi_u8_read,
+ .duplex = bfin_spi_u8_duplex,
+};
+
+static void bfin_spi_u16_write(struct bfin_spi_master *drv_data)
+{
+ dummy_read(drv_data);
+ while (drv_data->tx < drv_data->tx_end) {
+ bfin_write(&drv_data->regs->tfifo, (*(u16 *)drv_data->tx));
+ drv_data->tx += 2;
+ while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE)
+ cpu_relax();
+ bfin_read(&drv_data->regs->rfifo);
+ }
+}
+
+static void bfin_spi_u16_read(struct bfin_spi_master *drv_data)
+{
+ u32 tx_val = drv_data->cur_chip->tx_dummy_val;
+
+ dummy_read(drv_data);
+ while (drv_data->rx < drv_data->rx_end) {
+ bfin_write(&drv_data->regs->tfifo, tx_val);
+ while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE)
+ cpu_relax();
+ *(u16 *)drv_data->rx = bfin_read(&drv_data->regs->rfifo);
+ drv_data->rx += 2;
+ }
+}
+
+static void bfin_spi_u16_duplex(struct bfin_spi_master *drv_data)
+{
+ dummy_read(drv_data);
+ while (drv_data->rx < drv_data->rx_end) {
+ bfin_write(&drv_data->regs->tfifo, (*(u16 *)drv_data->tx));
+ drv_data->tx += 2;
+ while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE)
+ cpu_relax();
+ *(u16 *)drv_data->rx = bfin_read(&drv_data->regs->rfifo);
+ drv_data->rx += 2;
+ }
+}
+
+static const struct bfin_spi_transfer_ops bfin_bfin_spi_transfer_ops_u16 = {
+ .write = bfin_spi_u16_write,
+ .read = bfin_spi_u16_read,
+ .duplex = bfin_spi_u16_duplex,
+};
+
+static void bfin_spi_u32_write(struct bfin_spi_master *drv_data)
+{
+ dummy_read(drv_data);
+ while (drv_data->tx < drv_data->tx_end) {
+ bfin_write(&drv_data->regs->tfifo, (*(u32 *)drv_data->tx));
+ drv_data->tx += 4;
+ while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE)
+ cpu_relax();
+ bfin_read(&drv_data->regs->rfifo);
+ }
+}
+
+static void bfin_spi_u32_read(struct bfin_spi_master *drv_data)
+{
+ u32 tx_val = drv_data->cur_chip->tx_dummy_val;
+
+ dummy_read(drv_data);
+ while (drv_data->rx < drv_data->rx_end) {
+ bfin_write(&drv_data->regs->tfifo, tx_val);
+ while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE)
+ cpu_relax();
+ *(u32 *)drv_data->rx = bfin_read(&drv_data->regs->rfifo);
+ drv_data->rx += 4;
+ }
+}
+
+static void bfin_spi_u32_duplex(struct bfin_spi_master *drv_data)
+{
+ dummy_read(drv_data);
+ while (drv_data->rx < drv_data->rx_end) {
+ bfin_write(&drv_data->regs->tfifo, (*(u32 *)drv_data->tx));
+ drv_data->tx += 4;
+ while (bfin_read(&drv_data->regs->status) & SPI_STAT_RFE)
+ cpu_relax();
+ *(u32 *)drv_data->rx = bfin_read(&drv_data->regs->rfifo);
+ drv_data->rx += 4;
+ }
+}
+
+static const struct bfin_spi_transfer_ops bfin_bfin_spi_transfer_ops_u32 = {
+ .write = bfin_spi_u32_write,
+ .read = bfin_spi_u32_read,
+ .duplex = bfin_spi_u32_duplex,
+};
+
+
+/* test if there is more transfer to be done */
+static void bfin_spi_next_transfer(struct bfin_spi_master *drv)
+{
+ struct spi_message *msg = drv->cur_msg;
+ struct spi_transfer *t = drv->cur_transfer;
+
+ /* Move to next transfer */
+ if (t->transfer_list.next != &msg->transfers) {
+ drv->cur_transfer = list_entry(t->transfer_list.next,
+ struct spi_transfer, transfer_list);
+ drv->state = RUNNING_STATE;
+ } else {
+ drv->state = DONE_STATE;
+ drv->cur_transfer = NULL;
+ }
+}
+
+static void bfin_spi_giveback(struct bfin_spi_master *drv_data)
+{
+ struct bfin_spi_device *chip = drv_data->cur_chip;
+
+ bfin_spi_cs_deactive(drv_data, chip);
+ spi_finalize_current_message(drv_data->master);
+}
+
+static int bfin_spi_setup_transfer(struct bfin_spi_master *drv)
+{
+ struct spi_transfer *t = drv->cur_transfer;
+ u32 cr, cr_width;
+
+ if (t->tx_buf) {
+ drv->tx = (void *)t->tx_buf;
+ drv->tx_end = drv->tx + t->len;
+ } else {
+ drv->tx = NULL;
+ }
+
+ if (t->rx_buf) {
+ drv->rx = t->rx_buf;
+ drv->rx_end = drv->rx + t->len;
+ } else {
+ drv->rx = NULL;
+ }
+
+ drv->transfer_len = t->len;
+
+ /* bits per word setup */
+ switch (t->bits_per_word) {
+ case 8:
+ cr_width = SPI_CTL_SIZE08;
+ drv->ops = &bfin_bfin_spi_transfer_ops_u8;
+ break;
+ case 16:
+ cr_width = SPI_CTL_SIZE16;
+ drv->ops = &bfin_bfin_spi_transfer_ops_u16;
+ break;
+ case 32:
+ cr_width = SPI_CTL_SIZE32;
+ drv->ops = &bfin_bfin_spi_transfer_ops_u32;
+ break;
+ default:
+ return -EINVAL;
+ }
+ cr = bfin_read(&drv->regs->control) & ~SPI_CTL_SIZE;
+ cr |= cr_width;
+ bfin_write(&drv->regs->control, cr);
+
+ /* speed setup */
+ bfin_write(&drv->regs->clock,
+ hz_to_spi_clock(drv->sclk, t->speed_hz));
+ return 0;
+}
+
+static int bfin_spi_dma_xfer(struct bfin_spi_master *drv_data)
+{
+ struct spi_transfer *t = drv_data->cur_transfer;
+ struct spi_message *msg = drv_data->cur_msg;
+ struct bfin_spi_device *chip = drv_data->cur_chip;
+ u32 dma_config;
+ unsigned long word_count, word_size;
+ void *tx_buf, *rx_buf;
+
+ switch (t->bits_per_word) {
+ case 8:
+ dma_config = WDSIZE_8 | PSIZE_8;
+ word_count = drv_data->transfer_len;
+ word_size = 1;
+ break;
+ case 16:
+ dma_config = WDSIZE_16 | PSIZE_16;
+ word_count = drv_data->transfer_len / 2;
+ word_size = 2;
+ break;
+ default:
+ dma_config = WDSIZE_32 | PSIZE_32;
+ word_count = drv_data->transfer_len / 4;
+ word_size = 4;
+ break;
+ }
+
+ if (!drv_data->rx) {
+ tx_buf = drv_data->tx;
+ rx_buf = &drv_data->dummy_buffer;
+ drv_data->tx_dma_size = drv_data->transfer_len;
+ drv_data->rx_dma_size = sizeof(drv_data->dummy_buffer);
+ set_dma_x_modify(drv_data->tx_dma, word_size);
+ set_dma_x_modify(drv_data->rx_dma, 0);
+ } else if (!drv_data->tx) {
+ drv_data->dummy_buffer = chip->tx_dummy_val;
+ tx_buf = &drv_data->dummy_buffer;
+ rx_buf = drv_data->rx;
+ drv_data->tx_dma_size = sizeof(drv_data->dummy_buffer);
+ drv_data->rx_dma_size = drv_data->transfer_len;
+ set_dma_x_modify(drv_data->tx_dma, 0);
+ set_dma_x_modify(drv_data->rx_dma, word_size);
+ } else {
+ tx_buf = drv_data->tx;
+ rx_buf = drv_data->rx;
+ drv_data->tx_dma_size = drv_data->rx_dma_size
+ = drv_data->transfer_len;
+ set_dma_x_modify(drv_data->tx_dma, word_size);
+ set_dma_x_modify(drv_data->rx_dma, word_size);
+ }
+
+ drv_data->tx_dma_addr = dma_map_single(&msg->spi->dev,
+ (void *)tx_buf,
+ drv_data->tx_dma_size,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(&msg->spi->dev,
+ drv_data->tx_dma_addr))
+ return -ENOMEM;
+
+ drv_data->rx_dma_addr = dma_map_single(&msg->spi->dev,
+ (void *)rx_buf,
+ drv_data->rx_dma_size,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(&msg->spi->dev,
+ drv_data->rx_dma_addr)) {
+ dma_unmap_single(&msg->spi->dev,
+ drv_data->tx_dma_addr,
+ drv_data->tx_dma_size,
+ DMA_TO_DEVICE);
+ return -ENOMEM;
+ }
+
+ dummy_read(drv_data);
+ set_dma_x_count(drv_data->tx_dma, word_count);
+ set_dma_x_count(drv_data->rx_dma, word_count);
+ set_dma_start_addr(drv_data->tx_dma, drv_data->tx_dma_addr);
+ set_dma_start_addr(drv_data->rx_dma, drv_data->rx_dma_addr);
+ dma_config |= DMAFLOW_STOP | RESTART | DI_EN;
+ set_dma_config(drv_data->tx_dma, dma_config);
+ set_dma_config(drv_data->rx_dma, dma_config | WNR);
+ enable_dma(drv_data->tx_dma);
+ enable_dma(drv_data->rx_dma);
+ SSYNC();
+
+ bfin_write(&drv_data->regs->rx_control, SPI_RXCTL_REN | SPI_RXCTL_RDR_NE);
+ SSYNC();
+ bfin_write(&drv_data->regs->tx_control,
+ SPI_TXCTL_TEN | SPI_TXCTL_TTI | SPI_TXCTL_TDR_NF);
+
+ return 0;
+}
+
+static int bfin_spi_pio_xfer(struct bfin_spi_master *drv_data)
+{
+ struct spi_message *msg = drv_data->cur_msg;
+
+ if (!drv_data->rx) {
+ /* write only half duplex */
+ drv_data->ops->write(drv_data);
+ if (drv_data->tx != drv_data->tx_end)
+ return -EIO;
+ } else if (!drv_data->tx) {
+ /* read only half duplex */
+ drv_data->ops->read(drv_data);
+ if (drv_data->rx != drv_data->rx_end)
+ return -EIO;
+ } else {
+ /* full duplex mode */
+ drv_data->ops->duplex(drv_data);
+ if (drv_data->tx != drv_data->tx_end)
+ return -EIO;
+ }
+
+ if (!bfin_spi_flush(drv_data))
+ return -EIO;
+ msg->actual_length += drv_data->transfer_len;
+ tasklet_schedule(&drv_data->pump_transfers);
+ return 0;
+}
+
+static void bfin_spi_pump_transfers(unsigned long data)
+{
+ struct bfin_spi_master *drv_data = (struct bfin_spi_master *)data;
+ struct spi_message *msg = NULL;
+ struct spi_transfer *t = NULL;
+ struct bfin_spi_device *chip = NULL;
+ int ret;
+
+ /* Get current state information */
+ msg = drv_data->cur_msg;
+ t = drv_data->cur_transfer;
+ chip = drv_data->cur_chip;
+
+ /* Handle for abort */
+ if (drv_data->state == ERROR_STATE) {
+ msg->status = -EIO;
+ bfin_spi_giveback(drv_data);
+ return;
+ }
+
+ if (drv_data->state == RUNNING_STATE) {
+ if (t->delay_usecs)
+ udelay(t->delay_usecs);
+ if (t->cs_change)
+ bfin_spi_cs_deactive(drv_data, chip);
+ bfin_spi_next_transfer(drv_data);
+ t = drv_data->cur_transfer;
+ }
+ /* Handle end of message */
+ if (drv_data->state == DONE_STATE) {
+ msg->status = 0;
+ bfin_spi_giveback(drv_data);
+ return;
+ }
+
+ if ((t->len == 0) || (t->tx_buf == NULL && t->rx_buf == NULL)) {
+ /* Schedule next transfer tasklet */
+ tasklet_schedule(&drv_data->pump_transfers);
+ return;
+ }
+
+ ret = bfin_spi_setup_transfer(drv_data);
+ if (ret) {
+ msg->status = ret;
+ bfin_spi_giveback(drv_data);
+ }
+
+ bfin_write(&drv_data->regs->status, 0xFFFFFFFF);
+ bfin_spi_cs_active(drv_data, chip);
+ drv_data->state = RUNNING_STATE;
+
+ if (chip->enable_dma)
+ ret = bfin_spi_dma_xfer(drv_data);
+ else
+ ret = bfin_spi_pio_xfer(drv_data);
+ if (ret) {
+ msg->status = ret;
+ bfin_spi_giveback(drv_data);
+ }
+}
+
+static int bfin_spi_transfer_one_message(struct spi_master *master,
+ struct spi_message *m)
+{
+ struct bfin_spi_master *drv_data = spi_master_get_devdata(master);
+
+ drv_data->cur_msg = m;
+ drv_data->cur_chip = spi_get_ctldata(drv_data->cur_msg->spi);
+ bfin_spi_restore_state(drv_data);
+
+ drv_data->state = START_STATE;
+ drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next,
+ struct spi_transfer, transfer_list);
+
+ tasklet_schedule(&drv_data->pump_transfers);
+ return 0;
+}
+
+#define MAX_SPI_SSEL 7
+
+static const u16 ssel[][MAX_SPI_SSEL] = {
+ {P_SPI0_SSEL1, P_SPI0_SSEL2, P_SPI0_SSEL3,
+ P_SPI0_SSEL4, P_SPI0_SSEL5,
+ P_SPI0_SSEL6, P_SPI0_SSEL7},
+
+ {P_SPI1_SSEL1, P_SPI1_SSEL2, P_SPI1_SSEL3,
+ P_SPI1_SSEL4, P_SPI1_SSEL5,
+ P_SPI1_SSEL6, P_SPI1_SSEL7},
+
+ {P_SPI2_SSEL1, P_SPI2_SSEL2, P_SPI2_SSEL3,
+ P_SPI2_SSEL4, P_SPI2_SSEL5,
+ P_SPI2_SSEL6, P_SPI2_SSEL7},
+};
+
+static int bfin_spi_setup(struct spi_device *spi)
+{
+ struct bfin_spi_master *drv_data = spi_master_get_devdata(spi->master);
+ struct bfin_spi_device *chip = spi_get_ctldata(spi);
+ u32 bfin_ctl_reg = SPI_CTL_ODM | SPI_CTL_PSSE;
+ int ret = -EINVAL;
+
+ if (!chip) {
+ struct bfin_spi3_chip *chip_info = spi->controller_data;
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (!chip) {
+ dev_err(&spi->dev, "can not allocate chip data\n");
+ return -ENOMEM;
+ }
+ if (chip_info) {
+ if (chip_info->control & ~bfin_ctl_reg) {
+ dev_err(&spi->dev,
+ "do not set bits that the SPI framework manages\n");
+ goto error;
+ }
+ chip->control = chip_info->control;
+ chip->cs_chg_udelay = chip_info->cs_chg_udelay;
+ chip->tx_dummy_val = chip_info->tx_dummy_val;
+ chip->enable_dma = chip_info->enable_dma;
+ }
+ chip->cs = spi->chip_select;
+ if (chip->cs < MAX_CTRL_CS) {
+ chip->ssel = (1 << chip->cs) << 8;
+ ret = peripheral_request(ssel[spi->master->bus_num]
+ [chip->cs-1], dev_name(&spi->dev));
+ if (ret) {
+ dev_err(&spi->dev, "peripheral_request() error\n");
+ goto error;
+ }
+ } else {
+ chip->cs_gpio = chip->cs - MAX_CTRL_CS;
+ ret = gpio_request_one(chip->cs_gpio, GPIOF_OUT_INIT_HIGH,
+ dev_name(&spi->dev));
+ if (ret) {
+ dev_err(&spi->dev, "gpio_request_one() error\n");
+ goto error;
+ }
+ }
+ spi_set_ctldata(spi, chip);
+ }
+
+ /* force a default base state */
+ chip->control &= bfin_ctl_reg;
+
+ if (spi->mode & SPI_CPOL)
+ chip->control |= SPI_CTL_CPOL;
+ if (spi->mode & SPI_CPHA)
+ chip->control |= SPI_CTL_CPHA;
+ if (spi->mode & SPI_LSB_FIRST)
+ chip->control |= SPI_CTL_LSBF;
+ chip->control |= SPI_CTL_MSTR;
+ /* we choose software to controll cs */
+ chip->control &= ~SPI_CTL_ASSEL;
+
+ chip->clock = hz_to_spi_clock(drv_data->sclk, spi->max_speed_hz);
+
+ bfin_spi_cs_enable(drv_data, chip);
+ bfin_spi_cs_deactive(drv_data, chip);
+
+ return 0;
+error:
+ if (chip) {
+ kfree(chip);
+ spi_set_ctldata(spi, NULL);
+ }
+
+ return ret;
+}
+
+static void bfin_spi_cleanup(struct spi_device *spi)
+{
+ struct bfin_spi_device *chip = spi_get_ctldata(spi);
+ struct bfin_spi_master *drv_data = spi_master_get_devdata(spi->master);
+
+ if (!chip)
+ return;
+
+ if (chip->cs < MAX_CTRL_CS) {
+ peripheral_free(ssel[spi->master->bus_num]
+ [chip->cs-1]);
+ bfin_spi_cs_disable(drv_data, chip);
+ } else {
+ gpio_free(chip->cs_gpio);
+ }
+
+ kfree(chip);
+ spi_set_ctldata(spi, NULL);
+}
+
+static irqreturn_t bfin_spi_tx_dma_isr(int irq, void *dev_id)
+{
+ struct bfin_spi_master *drv_data = dev_id;
+ u32 dma_stat = get_dma_curr_irqstat(drv_data->tx_dma);
+
+ clear_dma_irqstat(drv_data->tx_dma);
+ if (dma_stat & DMA_DONE) {
+ drv_data->tx_num++;
+ } else {
+ dev_err(&drv_data->master->dev,
+ "spi tx dma error: %d\n", dma_stat);
+ if (drv_data->tx)
+ drv_data->state = ERROR_STATE;
+ }
+ bfin_write_and(&drv_data->regs->tx_control, ~SPI_TXCTL_TDR_NF);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t bfin_spi_rx_dma_isr(int irq, void *dev_id)
+{
+ struct bfin_spi_master *drv_data = dev_id;
+ struct spi_message *msg = drv_data->cur_msg;
+ u32 dma_stat = get_dma_curr_irqstat(drv_data->rx_dma);
+
+ clear_dma_irqstat(drv_data->rx_dma);
+ if (dma_stat & DMA_DONE) {
+ drv_data->rx_num++;
+ /* we may fail on tx dma */
+ if (drv_data->state != ERROR_STATE)
+ msg->actual_length += drv_data->transfer_len;
+ } else {
+ drv_data->state = ERROR_STATE;
+ dev_err(&drv_data->master->dev,
+ "spi rx dma error: %d\n", dma_stat);
+ }
+ bfin_write(&drv_data->regs->tx_control, 0);
+ bfin_write(&drv_data->regs->rx_control, 0);
+ if (drv_data->rx_num != drv_data->tx_num)
+ dev_dbg(&drv_data->master->dev,
+ "dma interrupt missing: tx=%d,rx=%d\n",
+ drv_data->tx_num, drv_data->rx_num);
+ tasklet_schedule(&drv_data->pump_transfers);
+ return IRQ_HANDLED;
+}
+
+static int bfin_spi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct bfin_spi3_master *info = dev_get_platdata(dev);
+ struct spi_master *master;
+ struct bfin_spi_master *drv_data;
+ struct resource *mem, *res;
+ unsigned int tx_dma, rx_dma;
+ unsigned long sclk;
+ int ret;
+
+ if (!info) {
+ dev_err(dev, "platform data missing!\n");
+ return -ENODEV;
+ }
+
+ sclk = get_sclk1();
+ if (!sclk) {
+ dev_err(dev, "can not get sclk1\n");
+ return -ENXIO;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!res) {
+ dev_err(dev, "can not get tx dma resource\n");
+ return -ENXIO;
+ }
+ tx_dma = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+ if (!res) {
+ dev_err(dev, "can not get rx dma resource\n");
+ return -ENXIO;
+ }
+ rx_dma = res->start;
+
+ /* allocate master with space for drv_data */
+ master = spi_alloc_master(dev, sizeof(*drv_data));
+ if (!master) {
+ dev_err(dev, "can not alloc spi_master\n");
+ return -ENOMEM;
+ }
+ platform_set_drvdata(pdev, master);
+
+ /* the mode bits supported by this driver */
+ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
+
+ master->bus_num = pdev->id;
+ master->num_chipselect = info->num_chipselect;
+ master->cleanup = bfin_spi_cleanup;
+ master->setup = bfin_spi_setup;
+ master->transfer_one_message = bfin_spi_transfer_one_message;
+ master->bits_per_word_mask = BIT(32 - 1) | BIT(16 - 1) | BIT(8 - 1);
+
+ drv_data = spi_master_get_devdata(master);
+ drv_data->master = master;
+ drv_data->tx_dma = tx_dma;
+ drv_data->rx_dma = rx_dma;
+ drv_data->pin_req = info->pin_req;
+ drv_data->sclk = sclk;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ drv_data->regs = devm_ioremap_resource(dev, mem);
+ if (IS_ERR(drv_data->regs)) {
+ ret = PTR_ERR(drv_data->regs);
+ goto err_put_master;
+ }
+
+ /* request tx and rx dma */
+ ret = request_dma(tx_dma, "SPI_TX_DMA");
+ if (ret) {
+ dev_err(dev, "can not request SPI TX DMA channel\n");
+ goto err_put_master;
+ }
+ set_dma_callback(tx_dma, bfin_spi_tx_dma_isr, drv_data);
+
+ ret = request_dma(rx_dma, "SPI_RX_DMA");
+ if (ret) {
+ dev_err(dev, "can not request SPI RX DMA channel\n");
+ goto err_free_tx_dma;
+ }
+ set_dma_callback(drv_data->rx_dma, bfin_spi_rx_dma_isr, drv_data);
+
+ /* request CLK, MOSI and MISO */
+ ret = peripheral_request_list(drv_data->pin_req, "bfin-spi3");
+ if (ret < 0) {
+ dev_err(dev, "can not request spi pins\n");
+ goto err_free_rx_dma;
+ }
+
+ bfin_write(&drv_data->regs->control, SPI_CTL_MSTR | SPI_CTL_CPHA);
+ bfin_write(&drv_data->regs->ssel, 0x0000FE00);
+ bfin_write(&drv_data->regs->delay, 0x0);
+
+ tasklet_init(&drv_data->pump_transfers,
+ bfin_spi_pump_transfers, (unsigned long)drv_data);
+ /* register with the SPI framework */
+ ret = spi_register_master(master);
+ if (ret) {
+ dev_err(dev, "can not register spi master\n");
+ goto err_free_peripheral;
+ }
+
+ return ret;
+
+err_free_peripheral:
+ peripheral_free_list(drv_data->pin_req);
+err_free_rx_dma:
+ free_dma(rx_dma);
+err_free_tx_dma:
+ free_dma(tx_dma);
+err_put_master:
+ spi_master_put(master);
+
+ return ret;
+}
+
+static int bfin_spi_remove(struct platform_device *pdev)
+{
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct bfin_spi_master *drv_data = spi_master_get_devdata(master);
+
+ bfin_spi_disable(drv_data);
+
+ peripheral_free_list(drv_data->pin_req);
+ free_dma(drv_data->rx_dma);
+ free_dma(drv_data->tx_dma);
+
+ spi_unregister_master(drv_data->master);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int bfin_spi_suspend(struct device *dev)
+{
+ struct spi_master *master = dev_get_drvdata(dev);
+ struct bfin_spi_master *drv_data = spi_master_get_devdata(master);
+
+ spi_master_suspend(master);
+
+ drv_data->control = bfin_read(&drv_data->regs->control);
+ drv_data->ssel = bfin_read(&drv_data->regs->ssel);
+
+ bfin_write(&drv_data->regs->control, SPI_CTL_MSTR | SPI_CTL_CPHA);
+ bfin_write(&drv_data->regs->ssel, 0x0000FE00);
+ dma_disable_irq(drv_data->rx_dma);
+ dma_disable_irq(drv_data->tx_dma);
+
+ return 0;
+}
+
+static int bfin_spi_resume(struct device *dev)
+{
+ struct spi_master *master = dev_get_drvdata(dev);
+ struct bfin_spi_master *drv_data = spi_master_get_devdata(master);
+ int ret = 0;
+
+ /* bootrom may modify spi and dma status when resume in spi boot mode */
+ disable_dma(drv_data->rx_dma);
+
+ dma_enable_irq(drv_data->rx_dma);
+ dma_enable_irq(drv_data->tx_dma);
+ bfin_write(&drv_data->regs->control, drv_data->control);
+ bfin_write(&drv_data->regs->ssel, drv_data->ssel);
+
+ ret = spi_master_resume(master);
+ if (ret) {
+ free_dma(drv_data->rx_dma);
+ free_dma(drv_data->tx_dma);
+ }
+
+ return ret;
+}
+#endif
+static const struct dev_pm_ops bfin_spi_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(bfin_spi_suspend, bfin_spi_resume)
+};
+
+MODULE_ALIAS("platform:bfin-spi3");
+static struct platform_driver bfin_spi_driver = {
+ .driver = {
+ .name = "bfin-spi3",
+ .owner = THIS_MODULE,
+ .pm = &bfin_spi_pm_ops,
+ },
+ .remove = bfin_spi_remove,
+};
+
+module_platform_driver_probe(bfin_spi_driver, bfin_spi_probe);
+
+MODULE_DESCRIPTION("Analog Devices SPI3 controller driver");
+MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
+MODULE_LICENSE("GPL v2");
struct resource *res;
int status = 0;
- platform_info = dev->platform_data;
+ platform_info = dev_get_platdata(dev);
/* Allocate master with space for drv_data */
master = spi_alloc_master(dev, sizeof(*drv_data));
* Drivers can provide word-at-a-time i/o primitives, or provide
* transfer-at-a-time ones to leverage dma or fifo hardware.
*/
-static void bitbang_work(struct work_struct *work)
+
+static int spi_bitbang_prepare_hardware(struct spi_master *spi)
{
- struct spi_bitbang *bitbang =
- container_of(work, struct spi_bitbang, work);
+ struct spi_bitbang *bitbang;
unsigned long flags;
- struct spi_message *m, *_m;
+
+ bitbang = spi_master_get_devdata(spi);
spin_lock_irqsave(&bitbang->lock, flags);
bitbang->busy = 1;
- list_for_each_entry_safe(m, _m, &bitbang->queue, queue) {
- struct spi_device *spi;
- unsigned nsecs;
- struct spi_transfer *t = NULL;
- unsigned tmp;
- unsigned cs_change;
- int status;
- int do_setup = -1;
-
- list_del(&m->queue);
- spin_unlock_irqrestore(&bitbang->lock, flags);
-
- /* FIXME this is made-up ... the correct value is known to
- * word-at-a-time bitbang code, and presumably chipselect()
- * should enforce these requirements too?
- */
- nsecs = 100;
+ spin_unlock_irqrestore(&bitbang->lock, flags);
- spi = m->spi;
- tmp = 0;
- cs_change = 1;
- status = 0;
+ return 0;
+}
- list_for_each_entry (t, &m->transfers, transfer_list) {
-
- /* override speed or wordsize? */
- if (t->speed_hz || t->bits_per_word)
- do_setup = 1;
-
- /* init (-1) or override (1) transfer params */
- if (do_setup != 0) {
- status = bitbang->setup_transfer(spi, t);
- if (status < 0)
- break;
- if (do_setup == -1)
- do_setup = 0;
- }
-
- /* set up default clock polarity, and activate chip;
- * this implicitly updates clock and spi modes as
- * previously recorded for this device via setup().
- * (and also deselects any other chip that might be
- * selected ...)
- */
- if (cs_change) {
- bitbang->chipselect(spi, BITBANG_CS_ACTIVE);
- ndelay(nsecs);
- }
- cs_change = t->cs_change;
- if (!t->tx_buf && !t->rx_buf && t->len) {
- status = -EINVAL;
- break;
- }
+static int spi_bitbang_transfer_one(struct spi_master *master,
+ struct spi_message *m)
+{
+ struct spi_bitbang *bitbang;
+ unsigned nsecs;
+ struct spi_transfer *t = NULL;
+ unsigned cs_change;
+ int status;
+ int do_setup = -1;
+ struct spi_device *spi = m->spi;
+
+ bitbang = spi_master_get_devdata(master);
+
+ /* FIXME this is made-up ... the correct value is known to
+ * word-at-a-time bitbang code, and presumably chipselect()
+ * should enforce these requirements too?
+ */
+ nsecs = 100;
- /* transfer data. the lower level code handles any
- * new dma mappings it needs. our caller always gave
- * us dma-safe buffers.
- */
- if (t->len) {
- /* REVISIT dma API still needs a designated
- * DMA_ADDR_INVALID; ~0 might be better.
- */
- if (!m->is_dma_mapped)
- t->rx_dma = t->tx_dma = 0;
- status = bitbang->txrx_bufs(spi, t);
- }
- if (status > 0)
- m->actual_length += status;
- if (status != t->len) {
- /* always report some kind of error */
- if (status >= 0)
- status = -EREMOTEIO;
+ cs_change = 1;
+ status = 0;
+
+ list_for_each_entry (t, &m->transfers, transfer_list) {
+
+ /* override speed or wordsize? */
+ if (t->speed_hz || t->bits_per_word)
+ do_setup = 1;
+
+ /* init (-1) or override (1) transfer params */
+ if (do_setup != 0) {
+ status = bitbang->setup_transfer(spi, t);
+ if (status < 0)
break;
- }
- status = 0;
-
- /* protocol tweaks before next transfer */
- if (t->delay_usecs)
- udelay(t->delay_usecs);
-
- if (cs_change && !list_is_last(&t->transfer_list, &m->transfers)) {
- /* sometimes a short mid-message deselect of the chip
- * may be needed to terminate a mode or command
- */
- ndelay(nsecs);
- bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
- ndelay(nsecs);
- }
+ if (do_setup == -1)
+ do_setup = 0;
}
- m->status = status;
- m->complete(m->context);
+ /* set up default clock polarity, and activate chip;
+ * this implicitly updates clock and spi modes as
+ * previously recorded for this device via setup().
+ * (and also deselects any other chip that might be
+ * selected ...)
+ */
+ if (cs_change) {
+ bitbang->chipselect(spi, BITBANG_CS_ACTIVE);
+ ndelay(nsecs);
+ }
+ cs_change = t->cs_change;
+ if (!t->tx_buf && !t->rx_buf && t->len) {
+ status = -EINVAL;
+ break;
+ }
- /* normally deactivate chipselect ... unless no error and
- * cs_change has hinted that the next message will probably
- * be for this chip too.
+ /* transfer data. the lower level code handles any
+ * new dma mappings it needs. our caller always gave
+ * us dma-safe buffers.
*/
- if (!(status == 0 && cs_change)) {
+ if (t->len) {
+ /* REVISIT dma API still needs a designated
+ * DMA_ADDR_INVALID; ~0 might be better.
+ */
+ if (!m->is_dma_mapped)
+ t->rx_dma = t->tx_dma = 0;
+ status = bitbang->txrx_bufs(spi, t);
+ }
+ if (status > 0)
+ m->actual_length += status;
+ if (status != t->len) {
+ /* always report some kind of error */
+ if (status >= 0)
+ status = -EREMOTEIO;
+ break;
+ }
+ status = 0;
+
+ /* protocol tweaks before next transfer */
+ if (t->delay_usecs)
+ udelay(t->delay_usecs);
+
+ if (cs_change && !list_is_last(&t->transfer_list, &m->transfers)) {
+ /* sometimes a short mid-message deselect of the chip
+ * may be needed to terminate a mode or command
+ */
ndelay(nsecs);
bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
ndelay(nsecs);
}
+ }
+
+ m->status = status;
- spin_lock_irqsave(&bitbang->lock, flags);
+ /* normally deactivate chipselect ... unless no error and
+ * cs_change has hinted that the next message will probably
+ * be for this chip too.
+ */
+ if (!(status == 0 && cs_change)) {
+ ndelay(nsecs);
+ bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
+ ndelay(nsecs);
}
- bitbang->busy = 0;
- spin_unlock_irqrestore(&bitbang->lock, flags);
+
+ spi_finalize_current_message(master);
+
+ return status;
}
-/**
- * spi_bitbang_transfer - default submit to transfer queue
- */
-int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m)
+static int spi_bitbang_unprepare_hardware(struct spi_master *spi)
{
- struct spi_bitbang *bitbang;
+ struct spi_bitbang *bitbang;
unsigned long flags;
- int status = 0;
- m->actual_length = 0;
- m->status = -EINPROGRESS;
-
- bitbang = spi_master_get_devdata(spi->master);
+ bitbang = spi_master_get_devdata(spi);
spin_lock_irqsave(&bitbang->lock, flags);
- if (!spi->max_speed_hz)
- status = -ENETDOWN;
- else {
- list_add_tail(&m->queue, &bitbang->queue);
- queue_work(bitbang->workqueue, &bitbang->work);
- }
+ bitbang->busy = 0;
spin_unlock_irqrestore(&bitbang->lock, flags);
- return status;
+ return 0;
}
-EXPORT_SYMBOL_GPL(spi_bitbang_transfer);
/*----------------------------------------------------------------------*/
int spi_bitbang_start(struct spi_bitbang *bitbang)
{
struct spi_master *master = bitbang->master;
- int status;
if (!master || !bitbang->chipselect)
return -EINVAL;
- INIT_WORK(&bitbang->work, bitbang_work);
spin_lock_init(&bitbang->lock);
- INIT_LIST_HEAD(&bitbang->queue);
if (!master->mode_bits)
master->mode_bits = SPI_CPOL | SPI_CPHA | bitbang->flags;
- if (!master->transfer)
- master->transfer = spi_bitbang_transfer;
+ if (master->transfer || master->transfer_one_message)
+ return -EINVAL;
+
+ master->prepare_transfer_hardware = spi_bitbang_prepare_hardware;
+ master->unprepare_transfer_hardware = spi_bitbang_unprepare_hardware;
+ master->transfer_one_message = spi_bitbang_transfer_one;
+
if (!bitbang->txrx_bufs) {
bitbang->use_dma = 0;
bitbang->txrx_bufs = spi_bitbang_bufs;
master->setup = spi_bitbang_setup;
master->cleanup = spi_bitbang_cleanup;
}
- } else if (!master->setup)
- return -EINVAL;
- if (master->transfer == spi_bitbang_transfer &&
- !bitbang->setup_transfer)
- return -EINVAL;
-
- /* this task is the only thing to touch the SPI bits */
- bitbang->busy = 0;
- bitbang->workqueue = create_singlethread_workqueue(
- dev_name(master->dev.parent));
- if (bitbang->workqueue == NULL) {
- status = -EBUSY;
- goto err1;
}
/* driver may get busy before register() returns, especially
* if someone registered boardinfo for devices
*/
- status = spi_register_master(master);
- if (status < 0)
- goto err2;
-
- return status;
-
-err2:
- destroy_workqueue(bitbang->workqueue);
-err1:
- return status;
+ return spi_register_master(master);
}
EXPORT_SYMBOL_GPL(spi_bitbang_start);
{
spi_unregister_master(bitbang->master);
- WARN_ON(!list_empty(&bitbang->queue));
-
- destroy_workqueue(bitbang->workqueue);
-
return 0;
}
EXPORT_SYMBOL_GPL(spi_bitbang_stop);
}
dev_err(&pdev->dev, "Failed to register master\n");
- devm_free_irq(&pdev->dev, IRQ_SSEOTI, hw);
clk_out:
- devm_clk_put(&pdev->dev, hw->spi_clk);
-
err_out:
while (--i >= 0)
if (gpio_is_valid(hw->chipselect[i]))
struct spi_master *master = platform_get_drvdata(pdev);
struct spi_clps711x_data *hw = spi_master_get_devdata(master);
- devm_free_irq(&pdev->dev, IRQ_SSEOTI, hw);
-
for (i = 0; i < master->num_chipselect; i++)
if (gpio_is_valid(hw->chipselect[i]))
gpio_free(hw->chipselect[i]);
- devm_clk_put(&pdev->dev, hw->spi_clk);
spi_unregister_master(master);
kfree(master);
}
-static int mcfqspi_prepare_transfer_hw(struct spi_master *master)
-{
- struct mcfqspi *mcfqspi = spi_master_get_devdata(master);
-
- pm_runtime_get_sync(mcfqspi->dev);
-
- return 0;
-}
-
-static int mcfqspi_unprepare_transfer_hw(struct spi_master *master)
-{
- struct mcfqspi *mcfqspi = spi_master_get_devdata(master);
-
- pm_runtime_put_sync(mcfqspi->dev);
-
- return 0;
-}
-
static int mcfqspi_setup(struct spi_device *spi)
{
if (spi->chip_select >= spi->master->num_chipselect) {
struct mcfqspi_platform_data *pdata;
int status;
- pdata = pdev->dev.platform_data;
+ pdata = dev_get_platdata(&pdev->dev);
if (!pdata) {
dev_dbg(&pdev->dev, "platform data is missing\n");
return -ENOENT;
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 16);
master->setup = mcfqspi_setup;
master->transfer_one_message = mcfqspi_transfer_one_message;
- master->prepare_transfer_hardware = mcfqspi_prepare_transfer_hw;
- master->unprepare_transfer_hardware = mcfqspi_unprepare_transfer_hw;
+ master->auto_runtime_pm = true;
platform_set_drvdata(pdev, master);
#ifdef CONFIG_PM_RUNTIME
static int mcfqspi_runtime_suspend(struct device *dev)
{
- struct mcfqspi *mcfqspi = platform_get_drvdata(to_platform_device(dev));
+ struct mcfqspi *mcfqspi = dev_get_drvdata(dev);
clk_disable(mcfqspi->clk);
static int mcfqspi_runtime_resume(struct device *dev)
{
- struct mcfqspi *mcfqspi = platform_get_drvdata(to_platform_device(dev));
+ struct mcfqspi *mcfqspi = dev_get_drvdata(dev);
clk_enable(mcfqspi->clk);
goto free_master;
}
- if (pdev->dev.platform_data) {
- pdata = pdev->dev.platform_data;
+ if (dev_get_platdata(&pdev->dev)) {
+ pdata = dev_get_platdata(&pdev->dev);
dspi->pdata = *pdata;
} else {
/* update dspi pdata with that from the DT */
--- /dev/null
+/*
+ * Copyright (C) 2012-2013 Uwe Kleine-Koenig for Pengutronix
+ *
+ * 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/kernel.h>
+#include <linux/io.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_bitbang.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_data/efm32-spi.h>
+
+#define DRIVER_NAME "efm32-spi"
+
+#define MASK_VAL(mask, val) ((val << __ffs(mask)) & mask)
+
+#define REG_CTRL 0x00
+#define REG_CTRL_SYNC 0x0001
+#define REG_CTRL_CLKPOL 0x0100
+#define REG_CTRL_CLKPHA 0x0200
+#define REG_CTRL_MSBF 0x0400
+#define REG_CTRL_TXBIL 0x1000
+
+#define REG_FRAME 0x04
+#define REG_FRAME_DATABITS__MASK 0x000f
+#define REG_FRAME_DATABITS(n) ((n) - 3)
+
+#define REG_CMD 0x0c
+#define REG_CMD_RXEN 0x0001
+#define REG_CMD_RXDIS 0x0002
+#define REG_CMD_TXEN 0x0004
+#define REG_CMD_TXDIS 0x0008
+#define REG_CMD_MASTEREN 0x0010
+
+#define REG_STATUS 0x10
+#define REG_STATUS_TXENS 0x0002
+#define REG_STATUS_TXC 0x0020
+#define REG_STATUS_TXBL 0x0040
+#define REG_STATUS_RXDATAV 0x0080
+
+#define REG_CLKDIV 0x14
+
+#define REG_RXDATAX 0x18
+#define REG_RXDATAX_RXDATA__MASK 0x01ff
+#define REG_RXDATAX_PERR 0x4000
+#define REG_RXDATAX_FERR 0x8000
+
+#define REG_TXDATA 0x34
+
+#define REG_IF 0x40
+#define REG_IF_TXBL 0x0002
+#define REG_IF_RXDATAV 0x0004
+
+#define REG_IFS 0x44
+#define REG_IFC 0x48
+#define REG_IEN 0x4c
+
+#define REG_ROUTE 0x54
+#define REG_ROUTE_RXPEN 0x0001
+#define REG_ROUTE_TXPEN 0x0002
+#define REG_ROUTE_CLKPEN 0x0008
+#define REG_ROUTE_LOCATION__MASK 0x0700
+#define REG_ROUTE_LOCATION(n) MASK_VAL(REG_ROUTE_LOCATION__MASK, (n))
+
+struct efm32_spi_ddata {
+ struct spi_bitbang bitbang;
+
+ spinlock_t lock;
+
+ struct clk *clk;
+ void __iomem *base;
+ unsigned int rxirq, txirq;
+ struct efm32_spi_pdata pdata;
+
+ /* irq data */
+ struct completion done;
+ const u8 *tx_buf;
+ u8 *rx_buf;
+ unsigned tx_len, rx_len;
+
+ /* chip selects */
+ unsigned csgpio[];
+};
+
+#define ddata_to_dev(ddata) (&(ddata->bitbang.master->dev))
+#define efm32_spi_vdbg(ddata, format, arg...) \
+ dev_vdbg(ddata_to_dev(ddata), format, ##arg)
+
+static void efm32_spi_write32(struct efm32_spi_ddata *ddata,
+ u32 value, unsigned offset)
+{
+ writel_relaxed(value, ddata->base + offset);
+}
+
+static u32 efm32_spi_read32(struct efm32_spi_ddata *ddata, unsigned offset)
+{
+ return readl_relaxed(ddata->base + offset);
+}
+
+static void efm32_spi_chipselect(struct spi_device *spi, int is_on)
+{
+ struct efm32_spi_ddata *ddata = spi_master_get_devdata(spi->master);
+ int value = !(spi->mode & SPI_CS_HIGH) == !(is_on == BITBANG_CS_ACTIVE);
+
+ gpio_set_value(ddata->csgpio[spi->chip_select], value);
+}
+
+static int efm32_spi_setup_transfer(struct spi_device *spi,
+ struct spi_transfer *t)
+{
+ struct efm32_spi_ddata *ddata = spi_master_get_devdata(spi->master);
+
+ unsigned bpw = t->bits_per_word ?: spi->bits_per_word;
+ unsigned speed = t->speed_hz ?: spi->max_speed_hz;
+ unsigned long clkfreq = clk_get_rate(ddata->clk);
+ u32 clkdiv;
+
+ efm32_spi_write32(ddata, REG_CTRL_SYNC | REG_CTRL_MSBF |
+ (spi->mode & SPI_CPHA ? REG_CTRL_CLKPHA : 0) |
+ (spi->mode & SPI_CPOL ? REG_CTRL_CLKPOL : 0), REG_CTRL);
+
+ efm32_spi_write32(ddata,
+ REG_FRAME_DATABITS(bpw), REG_FRAME);
+
+ if (2 * speed >= clkfreq)
+ clkdiv = 0;
+ else
+ clkdiv = 64 * (DIV_ROUND_UP(2 * clkfreq, speed) - 4);
+
+ if (clkdiv > (1U << 21))
+ return -EINVAL;
+
+ efm32_spi_write32(ddata, clkdiv, REG_CLKDIV);
+ efm32_spi_write32(ddata, REG_CMD_MASTEREN, REG_CMD);
+ efm32_spi_write32(ddata, REG_CMD_RXEN | REG_CMD_TXEN, REG_CMD);
+
+ return 0;
+}
+
+static void efm32_spi_tx_u8(struct efm32_spi_ddata *ddata)
+{
+ u8 val = 0;
+
+ if (ddata->tx_buf) {
+ val = *ddata->tx_buf;
+ ddata->tx_buf++;
+ }
+
+ ddata->tx_len--;
+ efm32_spi_write32(ddata, val, REG_TXDATA);
+ efm32_spi_vdbg(ddata, "%s: tx 0x%x\n", __func__, val);
+}
+
+static void efm32_spi_rx_u8(struct efm32_spi_ddata *ddata)
+{
+ u32 rxdata = efm32_spi_read32(ddata, REG_RXDATAX);
+ efm32_spi_vdbg(ddata, "%s: rx 0x%x\n", __func__, rxdata);
+
+ if (ddata->rx_buf) {
+ *ddata->rx_buf = rxdata;
+ ddata->rx_buf++;
+ }
+
+ ddata->rx_len--;
+}
+
+static void efm32_spi_filltx(struct efm32_spi_ddata *ddata)
+{
+ while (ddata->tx_len &&
+ ddata->tx_len + 2 > ddata->rx_len &&
+ efm32_spi_read32(ddata, REG_STATUS) & REG_STATUS_TXBL) {
+ efm32_spi_tx_u8(ddata);
+ }
+}
+
+static int efm32_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
+{
+ struct efm32_spi_ddata *ddata = spi_master_get_devdata(spi->master);
+ int ret = -EBUSY;
+
+ spin_lock_irq(&ddata->lock);
+
+ if (ddata->tx_buf || ddata->rx_buf)
+ goto out_unlock;
+
+ ddata->tx_buf = t->tx_buf;
+ ddata->rx_buf = t->rx_buf;
+ ddata->tx_len = ddata->rx_len =
+ t->len * DIV_ROUND_UP(t->bits_per_word, 8);
+
+ efm32_spi_filltx(ddata);
+
+ init_completion(&ddata->done);
+
+ efm32_spi_write32(ddata, REG_IF_TXBL | REG_IF_RXDATAV, REG_IEN);
+
+ spin_unlock_irq(&ddata->lock);
+
+ wait_for_completion(&ddata->done);
+
+ spin_lock_irq(&ddata->lock);
+
+ ret = t->len - max(ddata->tx_len, ddata->rx_len);
+
+ efm32_spi_write32(ddata, 0, REG_IEN);
+ ddata->tx_buf = ddata->rx_buf = NULL;
+
+out_unlock:
+ spin_unlock_irq(&ddata->lock);
+
+ return ret;
+}
+
+static irqreturn_t efm32_spi_rxirq(int irq, void *data)
+{
+ struct efm32_spi_ddata *ddata = data;
+ irqreturn_t ret = IRQ_NONE;
+
+ spin_lock(&ddata->lock);
+
+ while (ddata->rx_len > 0 &&
+ efm32_spi_read32(ddata, REG_STATUS) &
+ REG_STATUS_RXDATAV) {
+ efm32_spi_rx_u8(ddata);
+
+ ret = IRQ_HANDLED;
+ }
+
+ if (!ddata->rx_len) {
+ u32 ien = efm32_spi_read32(ddata, REG_IEN);
+
+ ien &= ~REG_IF_RXDATAV;
+
+ efm32_spi_write32(ddata, ien, REG_IEN);
+
+ complete(&ddata->done);
+ }
+
+ spin_unlock(&ddata->lock);
+
+ return ret;
+}
+
+static irqreturn_t efm32_spi_txirq(int irq, void *data)
+{
+ struct efm32_spi_ddata *ddata = data;
+
+ efm32_spi_vdbg(ddata,
+ "%s: txlen = %u, rxlen = %u, if=0x%08x, stat=0x%08x\n",
+ __func__, ddata->tx_len, ddata->rx_len,
+ efm32_spi_read32(ddata, REG_IF),
+ efm32_spi_read32(ddata, REG_STATUS));
+
+ spin_lock(&ddata->lock);
+
+ efm32_spi_filltx(ddata);
+
+ efm32_spi_vdbg(ddata, "%s: txlen = %u, rxlen = %u\n",
+ __func__, ddata->tx_len, ddata->rx_len);
+
+ if (!ddata->tx_len) {
+ u32 ien = efm32_spi_read32(ddata, REG_IEN);
+
+ ien &= ~REG_IF_TXBL;
+
+ efm32_spi_write32(ddata, ien, REG_IEN);
+ efm32_spi_vdbg(ddata, "disable TXBL\n");
+ }
+
+ spin_unlock(&ddata->lock);
+
+ return IRQ_HANDLED;
+}
+
+static const struct efm32_spi_pdata efm32_spi_pdata_default = {
+ .location = 1,
+};
+
+static u32 efm32_spi_get_configured_location(struct efm32_spi_ddata *ddata)
+{
+ u32 reg = efm32_spi_read32(ddata, REG_ROUTE);
+
+ return (reg & REG_ROUTE_LOCATION__MASK) >> __ffs(REG_ROUTE_LOCATION__MASK);
+}
+
+static int efm32_spi_probe_dt(struct platform_device *pdev,
+ struct spi_master *master, struct efm32_spi_ddata *ddata)
+{
+ struct device_node *np = pdev->dev.of_node;
+ u32 location;
+ int ret;
+
+ if (!np)
+ return 1;
+
+ ret = of_property_read_u32(np, "location", &location);
+ if (!ret) {
+ dev_dbg(&pdev->dev, "using location %u\n", location);
+ } else {
+ /* default to location configured in hardware */
+ location = efm32_spi_get_configured_location(ddata);
+
+ dev_info(&pdev->dev, "fall back to location %u\n", location);
+ }
+
+ ddata->pdata.location = location;
+
+ /* spi core takes care about the bus number using an alias */
+ master->bus_num = -1;
+
+ return 0;
+}
+
+static int efm32_spi_probe(struct platform_device *pdev)
+{
+ struct efm32_spi_ddata *ddata;
+ struct resource *res;
+ int ret;
+ struct spi_master *master;
+ struct device_node *np = pdev->dev.of_node;
+ unsigned int num_cs, i;
+
+ num_cs = of_gpio_named_count(np, "cs-gpios");
+
+ master = spi_alloc_master(&pdev->dev,
+ sizeof(*ddata) + num_cs * sizeof(unsigned));
+ if (!master) {
+ dev_dbg(&pdev->dev,
+ "failed to allocate spi master controller\n");
+ return -ENOMEM;
+ }
+ platform_set_drvdata(pdev, master);
+
+ master->dev.of_node = pdev->dev.of_node;
+
+ master->num_chipselect = num_cs;
+ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
+ master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16);
+
+ ddata = spi_master_get_devdata(master);
+
+ ddata->bitbang.master = spi_master_get(master);
+ ddata->bitbang.chipselect = efm32_spi_chipselect;
+ ddata->bitbang.setup_transfer = efm32_spi_setup_transfer;
+ ddata->bitbang.txrx_bufs = efm32_spi_txrx_bufs;
+
+ spin_lock_init(&ddata->lock);
+
+ ddata->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(ddata->clk)) {
+ ret = PTR_ERR(ddata->clk);
+ dev_err(&pdev->dev, "failed to get clock: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < num_cs; ++i) {
+ ret = of_get_named_gpio(np, "cs-gpios", i);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to get csgpio#%u (%d)\n",
+ i, ret);
+ goto err;
+ }
+ ddata->csgpio[i] = ret;
+ dev_dbg(&pdev->dev, "csgpio#%u = %u\n", i, ddata->csgpio[i]);
+ ret = devm_gpio_request_one(&pdev->dev, ddata->csgpio[i],
+ GPIOF_OUT_INIT_LOW, DRIVER_NAME);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "failed to configure csgpio#%u (%d)\n",
+ i, ret);
+ goto err;
+ }
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ ret = -ENODEV;
+ dev_err(&pdev->dev, "failed to determine base address\n");
+ goto err;
+ }
+
+ if (resource_size(res) < 60) {
+ ret = -EINVAL;
+ dev_err(&pdev->dev, "memory resource too small\n");
+ goto err;
+ }
+
+ ddata->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(ddata->base)) {
+ ret = PTR_ERR(ddata->base);
+ goto err;
+ }
+
+ ret = platform_get_irq(pdev, 0);
+ if (ret <= 0) {
+ dev_err(&pdev->dev, "failed to get rx irq (%d)\n", ret);
+ goto err;
+ }
+
+ ddata->rxirq = ret;
+
+ ret = platform_get_irq(pdev, 1);
+ if (ret <= 0)
+ ret = ddata->rxirq + 1;
+
+ ddata->txirq = ret;
+
+ ret = clk_prepare_enable(ddata->clk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to enable clock (%d)\n", ret);
+ goto err;
+ }
+
+ ret = efm32_spi_probe_dt(pdev, master, ddata);
+ if (ret > 0) {
+ /* not created by device tree */
+ const struct efm32_spi_pdata *pdata =
+ dev_get_platdata(&pdev->dev);
+
+ if (pdata)
+ ddata->pdata = *pdata;
+ else
+ ddata->pdata.location =
+ efm32_spi_get_configured_location(ddata);
+
+ master->bus_num = pdev->id;
+
+ } else if (ret < 0) {
+ goto err_disable_clk;
+ }
+
+ efm32_spi_write32(ddata, 0, REG_IEN);
+ efm32_spi_write32(ddata, REG_ROUTE_TXPEN | REG_ROUTE_RXPEN |
+ REG_ROUTE_CLKPEN |
+ REG_ROUTE_LOCATION(ddata->pdata.location), REG_ROUTE);
+
+ ret = request_irq(ddata->rxirq, efm32_spi_rxirq,
+ 0, DRIVER_NAME " rx", ddata);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register rxirq (%d)\n", ret);
+ goto err_disable_clk;
+ }
+
+ ret = request_irq(ddata->txirq, efm32_spi_txirq,
+ 0, DRIVER_NAME " tx", ddata);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register txirq (%d)\n", ret);
+ goto err_free_rx_irq;
+ }
+
+ ret = spi_bitbang_start(&ddata->bitbang);
+ if (ret) {
+ dev_err(&pdev->dev, "spi_bitbang_start failed (%d)\n", ret);
+
+ free_irq(ddata->txirq, ddata);
+err_free_rx_irq:
+ free_irq(ddata->rxirq, ddata);
+err_disable_clk:
+ clk_disable_unprepare(ddata->clk);
+err:
+ spi_master_put(master);
+ kfree(master);
+ }
+
+ return ret;
+}
+
+static int efm32_spi_remove(struct platform_device *pdev)
+{
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct efm32_spi_ddata *ddata = spi_master_get_devdata(master);
+
+ efm32_spi_write32(ddata, 0, REG_IEN);
+
+ free_irq(ddata->txirq, ddata);
+ free_irq(ddata->rxirq, ddata);
+ clk_disable_unprepare(ddata->clk);
+ spi_master_put(master);
+ kfree(master);
+
+ return 0;
+}
+
+static const struct of_device_id efm32_spi_dt_ids[] = {
+ {
+ .compatible = "efm32,spi",
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, efm32_spi_dt_ids);
+
+static struct platform_driver efm32_spi_driver = {
+ .probe = efm32_spi_probe,
+ .remove = efm32_spi_remove,
+
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = efm32_spi_dt_ids,
+ },
+};
+module_platform_driver(efm32_spi_driver);
+
+MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
+MODULE_DESCRIPTION("EFM32 SPI driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/workqueue.h>
#include <linux/sched.h>
#include <linux/scatterlist.h>
#include <linux/spi/spi.h>
/**
* struct ep93xx_spi - EP93xx SPI controller structure
- * @lock: spinlock that protects concurrent accesses to fields @running,
- * @current_msg and @msg_queue
* @pdev: pointer to platform device
* @clk: clock for the controller
* @regs_base: pointer to ioremap()'d registers
* @sspdr_phys: physical address of the SSPDR register
* @min_rate: minimum clock rate (in Hz) supported by the controller
* @max_rate: maximum clock rate (in Hz) supported by the controller
- * @running: is the queue running
- * @wq: workqueue used by the driver
- * @msg_work: work that is queued for the driver
* @wait: wait here until given transfer is completed
- * @msg_queue: queue for the messages
* @current_msg: message that is currently processed (or %NULL if none)
* @tx: current byte in transfer to transmit
* @rx: current byte in transfer to receive
* @tx_sgt: sg table for TX transfers
* @zeropage: dummy page used as RX buffer when only TX buffer is passed in by
* the client
- *
- * This structure holds EP93xx SPI controller specific information. When
- * @running is %true, driver accepts transfer requests from protocol drivers.
- * @current_msg is used to hold pointer to the message that is currently
- * processed. If @current_msg is %NULL, it means that no processing is going
- * on.
- *
- * Most of the fields are only written once and they can be accessed without
- * taking the @lock. Fields that are accessed concurrently are: @current_msg,
- * @running, and @msg_queue.
*/
struct ep93xx_spi {
- spinlock_t lock;
const struct platform_device *pdev;
struct clk *clk;
void __iomem *regs_base;
unsigned long sspdr_phys;
unsigned long min_rate;
unsigned long max_rate;
- bool running;
- struct workqueue_struct *wq;
- struct work_struct msg_work;
struct completion wait;
- struct list_head msg_queue;
struct spi_message *current_msg;
size_t tx;
size_t rx;
/**
* struct ep93xx_spi_chip - SPI device hardware settings
* @spi: back pointer to the SPI device
- * @rate: max rate in hz this chip supports
- * @div_cpsr: cpsr (pre-scaler) divider
- * @div_scr: scr divider
- * @dss: bits per word (4 - 16 bits)
* @ops: private chip operations
- *
- * This structure is used to store hardware register specific settings for each
- * SPI device. Settings are written to hardware by function
- * ep93xx_spi_chip_setup().
*/
struct ep93xx_spi_chip {
const struct spi_device *spi;
- unsigned long rate;
- u8 div_cpsr;
- u8 div_scr;
- u8 dss;
struct ep93xx_spi_chip_ops *ops;
};
/* converts bits per word to CR0.DSS value */
#define bits_per_word_to_dss(bpw) ((bpw) - 1)
-static inline void
-ep93xx_spi_write_u8(const struct ep93xx_spi *espi, u16 reg, u8 value)
+static void ep93xx_spi_write_u8(const struct ep93xx_spi *espi,
+ u16 reg, u8 value)
{
- __raw_writeb(value, espi->regs_base + reg);
+ writeb(value, espi->regs_base + reg);
}
-static inline u8
-ep93xx_spi_read_u8(const struct ep93xx_spi *spi, u16 reg)
+static u8 ep93xx_spi_read_u8(const struct ep93xx_spi *spi, u16 reg)
{
- return __raw_readb(spi->regs_base + reg);
+ return readb(spi->regs_base + reg);
}
-static inline void
-ep93xx_spi_write_u16(const struct ep93xx_spi *espi, u16 reg, u16 value)
+static void ep93xx_spi_write_u16(const struct ep93xx_spi *espi,
+ u16 reg, u16 value)
{
- __raw_writew(value, espi->regs_base + reg);
+ writew(value, espi->regs_base + reg);
}
-static inline u16
-ep93xx_spi_read_u16(const struct ep93xx_spi *spi, u16 reg)
+static u16 ep93xx_spi_read_u16(const struct ep93xx_spi *spi, u16 reg)
{
- return __raw_readw(spi->regs_base + reg);
+ return readw(spi->regs_base + reg);
}
static int ep93xx_spi_enable(const struct ep93xx_spi *espi)
/**
* ep93xx_spi_calc_divisors() - calculates SPI clock divisors
* @espi: ep93xx SPI controller struct
- * @chip: divisors are calculated for this chip
* @rate: desired SPI output clock rate
- *
- * Function calculates cpsr (clock pre-scaler) and scr divisors based on
- * given @rate and places them to @chip->div_cpsr and @chip->div_scr. If,
- * for some reason, divisors cannot be calculated nothing is stored and
- * %-EINVAL is returned.
+ * @div_cpsr: pointer to return the cpsr (pre-scaler) divider
+ * @div_scr: pointer to return the scr divider
*/
static int ep93xx_spi_calc_divisors(const struct ep93xx_spi *espi,
- struct ep93xx_spi_chip *chip,
- unsigned long rate)
+ unsigned long rate,
+ u8 *div_cpsr, u8 *div_scr)
{
unsigned long spi_clk_rate = clk_get_rate(espi->clk);
int cpsr, scr;
/*
* Make sure that max value is between values supported by the
* controller. Note that minimum value is already checked in
- * ep93xx_spi_transfer().
+ * ep93xx_spi_transfer_one_message().
*/
rate = clamp(rate, espi->min_rate, espi->max_rate);
for (cpsr = 2; cpsr <= 254; cpsr += 2) {
for (scr = 0; scr <= 255; scr++) {
if ((spi_clk_rate / (cpsr * (scr + 1))) <= rate) {
- chip->div_scr = (u8)scr;
- chip->div_cpsr = (u8)cpsr;
+ *div_scr = (u8)scr;
+ *div_cpsr = (u8)cpsr;
return 0;
}
}
spi_set_ctldata(spi, chip);
}
- if (spi->max_speed_hz != chip->rate) {
- int err;
-
- err = ep93xx_spi_calc_divisors(espi, chip, spi->max_speed_hz);
- if (err != 0) {
- spi_set_ctldata(spi, NULL);
- kfree(chip);
- return err;
- }
- chip->rate = spi->max_speed_hz;
- }
-
- chip->dss = bits_per_word_to_dss(spi->bits_per_word);
-
ep93xx_spi_cs_control(spi, false);
return 0;
}
-/**
- * ep93xx_spi_transfer() - queue message to be transferred
- * @spi: target SPI device
- * @msg: message to be transferred
- *
- * This function is called by SPI device drivers when they are going to transfer
- * a new message. It simply puts the message in the queue and schedules
- * workqueue to perform the actual transfer later on.
- *
- * Returns %0 on success and negative error in case of failure.
- */
-static int ep93xx_spi_transfer(struct spi_device *spi, struct spi_message *msg)
-{
- struct ep93xx_spi *espi = spi_master_get_devdata(spi->master);
- struct spi_transfer *t;
- unsigned long flags;
-
- if (!msg || !msg->complete)
- return -EINVAL;
-
- /* first validate each transfer */
- list_for_each_entry(t, &msg->transfers, transfer_list) {
- if (t->speed_hz && t->speed_hz < espi->min_rate)
- return -EINVAL;
- }
-
- /*
- * Now that we own the message, let's initialize it so that it is
- * suitable for us. We use @msg->status to signal whether there was
- * error in transfer and @msg->state is used to hold pointer to the
- * current transfer (or %NULL if no active current transfer).
- */
- msg->state = NULL;
- msg->status = 0;
- msg->actual_length = 0;
-
- spin_lock_irqsave(&espi->lock, flags);
- if (!espi->running) {
- spin_unlock_irqrestore(&espi->lock, flags);
- return -ESHUTDOWN;
- }
- list_add_tail(&msg->queue, &espi->msg_queue);
- queue_work(espi->wq, &espi->msg_work);
- spin_unlock_irqrestore(&espi->lock, flags);
-
- return 0;
-}
-
/**
* ep93xx_spi_cleanup() - cleans up master controller specific state
* @spi: SPI device to cleanup
* ep93xx_spi_chip_setup() - configures hardware according to given @chip
* @espi: ep93xx SPI controller struct
* @chip: chip specific settings
- *
- * This function sets up the actual hardware registers with settings given in
- * @chip. Note that no validation is done so make sure that callers validate
- * settings before calling this.
+ * @speed_hz: transfer speed
+ * @bits_per_word: transfer bits_per_word
*/
-static void ep93xx_spi_chip_setup(const struct ep93xx_spi *espi,
- const struct ep93xx_spi_chip *chip)
+static int ep93xx_spi_chip_setup(const struct ep93xx_spi *espi,
+ const struct ep93xx_spi_chip *chip,
+ u32 speed_hz, u8 bits_per_word)
{
+ u8 dss = bits_per_word_to_dss(bits_per_word);
+ u8 div_cpsr = 0;
+ u8 div_scr = 0;
u16 cr0;
+ int err;
- cr0 = chip->div_scr << SSPCR0_SCR_SHIFT;
+ err = ep93xx_spi_calc_divisors(espi, speed_hz, &div_cpsr, &div_scr);
+ if (err)
+ return err;
+
+ cr0 = div_scr << SSPCR0_SCR_SHIFT;
cr0 |= (chip->spi->mode & (SPI_CPHA|SPI_CPOL)) << SSPCR0_MODE_SHIFT;
- cr0 |= chip->dss;
+ cr0 |= dss;
dev_dbg(&espi->pdev->dev, "setup: mode %d, cpsr %d, scr %d, dss %d\n",
- chip->spi->mode, chip->div_cpsr, chip->div_scr, chip->dss);
+ chip->spi->mode, div_cpsr, div_scr, dss);
dev_dbg(&espi->pdev->dev, "setup: cr0 %#x", cr0);
- ep93xx_spi_write_u8(espi, SSPCPSR, chip->div_cpsr);
+ ep93xx_spi_write_u8(espi, SSPCPSR, div_cpsr);
ep93xx_spi_write_u16(espi, SSPCR0, cr0);
-}
-
-static inline int bits_per_word(const struct ep93xx_spi *espi)
-{
- struct spi_message *msg = espi->current_msg;
- struct spi_transfer *t = msg->state;
- return t->bits_per_word;
+ return 0;
}
static void ep93xx_do_write(struct ep93xx_spi *espi, struct spi_transfer *t)
{
- if (bits_per_word(espi) > 8) {
+ if (t->bits_per_word > 8) {
u16 tx_val = 0;
if (t->tx_buf)
static void ep93xx_do_read(struct ep93xx_spi *espi, struct spi_transfer *t)
{
- if (bits_per_word(espi) > 8) {
+ if (t->bits_per_word > 8) {
u16 rx_val;
rx_val = ep93xx_spi_read_u16(espi, SSPDR);
size_t len = t->len;
int i, ret, nents;
- if (bits_per_word(espi) > 8)
+ if (t->bits_per_word > 8)
buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
else
buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE;
}
if (WARN_ON(len)) {
- dev_warn(&espi->pdev->dev, "len = %d expected 0!", len);
+ dev_warn(&espi->pdev->dev, "len = %zu expected 0!", len);
return ERR_PTR(-EINVAL);
}
struct spi_transfer *t)
{
struct ep93xx_spi_chip *chip = spi_get_ctldata(msg->spi);
+ int err;
msg->state = t;
- /*
- * Handle any transfer specific settings if needed. We use
- * temporary chip settings here and restore original later when
- * the transfer is finished.
- */
- if (t->speed_hz || t->bits_per_word) {
- struct ep93xx_spi_chip tmp_chip = *chip;
-
- if (t->speed_hz) {
- int err;
-
- err = ep93xx_spi_calc_divisors(espi, &tmp_chip,
- t->speed_hz);
- if (err) {
- dev_err(&espi->pdev->dev,
- "failed to adjust speed\n");
- msg->status = err;
- return;
- }
- }
-
- if (t->bits_per_word)
- tmp_chip.dss = bits_per_word_to_dss(t->bits_per_word);
-
- /*
- * Set up temporary new hw settings for this transfer.
- */
- ep93xx_spi_chip_setup(espi, &tmp_chip);
+ err = ep93xx_spi_chip_setup(espi, chip, t->speed_hz, t->bits_per_word);
+ if (err) {
+ dev_err(&espi->pdev->dev,
+ "failed to setup chip for transfer\n");
+ msg->status = err;
+ return;
}
espi->rx = 0;
ep93xx_spi_cs_control(msg->spi, true);
}
}
-
- if (t->speed_hz || t->bits_per_word)
- ep93xx_spi_chip_setup(espi, chip);
}
/*
espi->fifo_level = 0;
/*
- * Update SPI controller registers according to spi device and assert
- * the chipselect.
+ * Assert the chipselect.
*/
- ep93xx_spi_chip_setup(espi, spi_get_ctldata(msg->spi));
ep93xx_spi_cs_control(msg->spi, true);
list_for_each_entry(t, &msg->transfers, transfer_list) {
ep93xx_spi_disable(espi);
}
-#define work_to_espi(work) (container_of((work), struct ep93xx_spi, msg_work))
-
-/**
- * ep93xx_spi_work() - EP93xx SPI workqueue worker function
- * @work: work struct
- *
- * Workqueue worker function. This function is called when there are new
- * SPI messages to be processed. Message is taken out from the queue and then
- * passed to ep93xx_spi_process_message().
- *
- * After message is transferred, protocol driver is notified by calling
- * @msg->complete(). In case of error, @msg->status is set to negative error
- * number, otherwise it contains zero (and @msg->actual_length is updated).
- */
-static void ep93xx_spi_work(struct work_struct *work)
+static int ep93xx_spi_transfer_one_message(struct spi_master *master,
+ struct spi_message *msg)
{
- struct ep93xx_spi *espi = work_to_espi(work);
- struct spi_message *msg;
+ struct ep93xx_spi *espi = spi_master_get_devdata(master);
+ struct spi_transfer *t;
- spin_lock_irq(&espi->lock);
- if (!espi->running || espi->current_msg ||
- list_empty(&espi->msg_queue)) {
- spin_unlock_irq(&espi->lock);
- return;
+ /* first validate each transfer */
+ list_for_each_entry(t, &msg->transfers, transfer_list) {
+ if (t->speed_hz < espi->min_rate)
+ return -EINVAL;
}
- msg = list_first_entry(&espi->msg_queue, struct spi_message, queue);
- list_del_init(&msg->queue);
- espi->current_msg = msg;
- spin_unlock_irq(&espi->lock);
- ep93xx_spi_process_message(espi, msg);
+ msg->state = NULL;
+ msg->status = 0;
+ msg->actual_length = 0;
- /*
- * Update the current message and re-schedule ourselves if there are
- * more messages in the queue.
- */
- spin_lock_irq(&espi->lock);
+ espi->current_msg = msg;
+ ep93xx_spi_process_message(espi, msg);
espi->current_msg = NULL;
- if (espi->running && !list_empty(&espi->msg_queue))
- queue_work(espi->wq, &espi->msg_work);
- spin_unlock_irq(&espi->lock);
- /* notify the protocol driver that we are done with this message */
- msg->complete(msg->context);
+ spi_finalize_current_message(master);
+
+ return 0;
}
static irqreturn_t ep93xx_spi_interrupt(int irq, void *dev_id)
int irq;
int error;
- info = pdev->dev.platform_data;
+ info = dev_get_platdata(&pdev->dev);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "failed to get irq resources\n");
+ return -EBUSY;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "unable to get iomem resource\n");
+ return -ENODEV;
+ }
master = spi_alloc_master(&pdev->dev, sizeof(*espi));
- if (!master) {
- dev_err(&pdev->dev, "failed to allocate spi master\n");
+ if (!master)
return -ENOMEM;
- }
master->setup = ep93xx_spi_setup;
- master->transfer = ep93xx_spi_transfer;
+ master->transfer_one_message = ep93xx_spi_transfer_one_message;
master->cleanup = ep93xx_spi_cleanup;
master->bus_num = pdev->id;
master->num_chipselect = info->num_chipselect;
espi = spi_master_get_devdata(master);
- espi->clk = clk_get(&pdev->dev, NULL);
+ espi->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(espi->clk)) {
dev_err(&pdev->dev, "unable to get spi clock\n");
error = PTR_ERR(espi->clk);
goto fail_release_master;
}
- spin_lock_init(&espi->lock);
init_completion(&espi->wait);
/*
espi->min_rate = clk_get_rate(espi->clk) / (254 * 256);
espi->pdev = pdev;
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- error = -EBUSY;
- dev_err(&pdev->dev, "failed to get irq resources\n");
- goto fail_put_clock;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "unable to get iomem resource\n");
- error = -ENODEV;
- goto fail_put_clock;
- }
-
espi->sspdr_phys = res->start + SSPDR;
espi->regs_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(espi->regs_base)) {
error = PTR_ERR(espi->regs_base);
- goto fail_put_clock;
+ goto fail_release_master;
}
error = devm_request_irq(&pdev->dev, irq, ep93xx_spi_interrupt,
0, "ep93xx-spi", espi);
if (error) {
dev_err(&pdev->dev, "failed to request irq\n");
- goto fail_put_clock;
+ goto fail_release_master;
}
if (info->use_dma && ep93xx_spi_setup_dma(espi))
dev_warn(&pdev->dev, "DMA setup failed. Falling back to PIO\n");
- espi->wq = create_singlethread_workqueue("ep93xx_spid");
- if (!espi->wq) {
- dev_err(&pdev->dev, "unable to create workqueue\n");
- error = -ENOMEM;
- goto fail_free_dma;
- }
- INIT_WORK(&espi->msg_work, ep93xx_spi_work);
- INIT_LIST_HEAD(&espi->msg_queue);
- espi->running = true;
-
/* make sure that the hardware is disabled */
ep93xx_spi_write_u8(espi, SSPCR1, 0);
error = spi_register_master(master);
if (error) {
dev_err(&pdev->dev, "failed to register SPI master\n");
- goto fail_free_queue;
+ goto fail_free_dma;
}
dev_info(&pdev->dev, "EP93xx SPI Controller at 0x%08lx irq %d\n",
return 0;
-fail_free_queue:
- destroy_workqueue(espi->wq);
fail_free_dma:
ep93xx_spi_release_dma(espi);
-fail_put_clock:
- clk_put(espi->clk);
fail_release_master:
spi_master_put(master);
struct spi_master *master = platform_get_drvdata(pdev);
struct ep93xx_spi *espi = spi_master_get_devdata(master);
- spin_lock_irq(&espi->lock);
- espi->running = false;
- spin_unlock_irq(&espi->lock);
-
- destroy_workqueue(espi->wq);
-
- /*
- * Complete remaining messages with %-ESHUTDOWN status.
- */
- spin_lock_irq(&espi->lock);
- while (!list_empty(&espi->msg_queue)) {
- struct spi_message *msg;
-
- msg = list_first_entry(&espi->msg_queue,
- struct spi_message, queue);
- list_del_init(&msg->queue);
- msg->status = -ESHUTDOWN;
- spin_unlock_irq(&espi->lock);
- msg->complete(msg->context);
- spin_lock_irq(&espi->lock);
- }
- spin_unlock_irq(&espi->lock);
-
ep93xx_spi_release_dma(espi);
- clk_put(espi->clk);
spi_unregister_master(master);
return 0;
--- /dev/null
+/*
+ * drivers/spi/spi-fsl-dspi.c
+ *
+ * Copyright 2013 Freescale Semiconductor, Inc.
+ *
+ * Freescale DSPI driver
+ * This file contains a driver for the Freescale DSPI
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_bitbang.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#define DRIVER_NAME "fsl-dspi"
+
+#define TRAN_STATE_RX_VOID 0x01
+#define TRAN_STATE_TX_VOID 0x02
+#define TRAN_STATE_WORD_ODD_NUM 0x04
+
+#define DSPI_FIFO_SIZE 4
+
+#define SPI_MCR 0x00
+#define SPI_MCR_MASTER (1 << 31)
+#define SPI_MCR_PCSIS (0x3F << 16)
+#define SPI_MCR_CLR_TXF (1 << 11)
+#define SPI_MCR_CLR_RXF (1 << 10)
+
+#define SPI_TCR 0x08
+
+#define SPI_CTAR(x) (0x0c + (x * 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_CTAR_LSBFE(x) ((x) << 24)
+#define SPI_CTAR_PCSSCR(x) (((x) & 0x00000003) << 22)
+#define SPI_CTAR_PASC(x) (((x) & 0x00000003) << 20)
+#define SPI_CTAR_PDT(x) (((x) & 0x00000003) << 18)
+#define SPI_CTAR_PBR(x) (((x) & 0x00000003) << 16)
+#define SPI_CTAR_CSSCK(x) (((x) & 0x0000000f) << 12)
+#define SPI_CTAR_ASC(x) (((x) & 0x0000000f) << 8)
+#define SPI_CTAR_DT(x) (((x) & 0x0000000f) << 4)
+#define SPI_CTAR_BR(x) ((x) & 0x0000000f)
+
+#define SPI_CTAR0_SLAVE 0x0c
+
+#define SPI_SR 0x2c
+#define SPI_SR_EOQF 0x10000000
+
+#define SPI_RSER 0x30
+#define SPI_RSER_EOQFE 0x10000000
+
+#define SPI_PUSHR 0x34
+#define SPI_PUSHR_CONT (1 << 31)
+#define SPI_PUSHR_CTAS(x) (((x) & 0x00000007) << 28)
+#define SPI_PUSHR_EOQ (1 << 27)
+#define SPI_PUSHR_CTCNT (1 << 26)
+#define SPI_PUSHR_PCS(x) (((1 << x) & 0x0000003f) << 16)
+#define SPI_PUSHR_TXDATA(x) ((x) & 0x0000ffff)
+
+#define SPI_PUSHR_SLAVE 0x34
+
+#define SPI_POPR 0x38
+#define SPI_POPR_RXDATA(x) ((x) & 0x0000ffff)
+
+#define SPI_TXFR0 0x3c
+#define SPI_TXFR1 0x40
+#define SPI_TXFR2 0x44
+#define SPI_TXFR3 0x48
+#define SPI_RXFR0 0x7c
+#define SPI_RXFR1 0x80
+#define SPI_RXFR2 0x84
+#define SPI_RXFR3 0x88
+
+#define SPI_FRAME_BITS(bits) SPI_CTAR_FMSZ((bits) - 1)
+#define SPI_FRAME_BITS_MASK SPI_CTAR_FMSZ(0xf)
+#define SPI_FRAME_BITS_16 SPI_CTAR_FMSZ(0xf)
+#define SPI_FRAME_BITS_8 SPI_CTAR_FMSZ(0x7)
+
+#define SPI_CS_INIT 0x01
+#define SPI_CS_ASSERT 0x02
+#define SPI_CS_DROP 0x04
+
+struct chip_data {
+ u32 mcr_val;
+ u32 ctar_val;
+ u16 void_write_data;
+};
+
+struct fsl_dspi {
+ struct spi_bitbang bitbang;
+ struct platform_device *pdev;
+
+ void *base;
+ int irq;
+ struct clk *clk;
+
+ struct spi_transfer *cur_transfer;
+ struct chip_data *cur_chip;
+ size_t len;
+ void *tx;
+ void *tx_end;
+ void *rx;
+ void *rx_end;
+ char dataflags;
+ u8 cs;
+ u16 void_write_data;
+
+ wait_queue_head_t waitq;
+ u32 waitflags;
+};
+
+static inline int is_double_byte_mode(struct fsl_dspi *dspi)
+{
+ return ((readl(dspi->base + SPI_CTAR(dspi->cs)) & SPI_FRAME_BITS_MASK)
+ == SPI_FRAME_BITS(8)) ? 0 : 1;
+}
+
+static void set_bit_mode(struct fsl_dspi *dspi, unsigned char bits)
+{
+ u32 temp;
+
+ temp = readl(dspi->base + SPI_CTAR(dspi->cs));
+ temp &= ~SPI_FRAME_BITS_MASK;
+ temp |= SPI_FRAME_BITS(bits);
+ writel(temp, dspi->base + SPI_CTAR(dspi->cs));
+}
+
+static void hz_to_spi_baud(char *pbr, char *br, int speed_hz,
+ unsigned long clkrate)
+{
+ /* Valid baud rate pre-scaler values */
+ int pbr_tbl[4] = {2, 3, 5, 7};
+ int brs[16] = { 2, 4, 6, 8,
+ 16, 32, 64, 128,
+ 256, 512, 1024, 2048,
+ 4096, 8192, 16384, 32768 };
+ int temp, i = 0, j = 0;
+
+ temp = clkrate / 2 / speed_hz;
+
+ for (i = 0; i < ARRAY_SIZE(pbr_tbl); i++)
+ for (j = 0; j < ARRAY_SIZE(brs); j++) {
+ if (pbr_tbl[i] * brs[j] >= temp) {
+ *pbr = i;
+ *br = j;
+ return;
+ }
+ }
+
+ pr_warn("Can not find valid buad rate,speed_hz is %d,clkrate is %ld\
+ ,we use the max prescaler value.\n", speed_hz, clkrate);
+ *pbr = ARRAY_SIZE(pbr_tbl) - 1;
+ *br = ARRAY_SIZE(brs) - 1;
+}
+
+static int dspi_transfer_write(struct fsl_dspi *dspi)
+{
+ int tx_count = 0;
+ int tx_word;
+ u16 d16;
+ u8 d8;
+ u32 dspi_pushr = 0;
+ int first = 1;
+
+ tx_word = is_double_byte_mode(dspi);
+
+ /* If we are in word mode, but only have a single byte to transfer
+ * then switch to byte mode temporarily. Will switch back at the
+ * end of the transfer.
+ */
+ if (tx_word && (dspi->len == 1)) {
+ dspi->dataflags |= TRAN_STATE_WORD_ODD_NUM;
+ set_bit_mode(dspi, 8);
+ tx_word = 0;
+ }
+
+ while (dspi->len && (tx_count < DSPI_FIFO_SIZE)) {
+ if (tx_word) {
+ if (dspi->len == 1)
+ break;
+
+ if (!(dspi->dataflags & TRAN_STATE_TX_VOID)) {
+ d16 = *(u16 *)dspi->tx;
+ dspi->tx += 2;
+ } else {
+ d16 = dspi->void_write_data;
+ }
+
+ dspi_pushr = SPI_PUSHR_TXDATA(d16) |
+ SPI_PUSHR_PCS(dspi->cs) |
+ SPI_PUSHR_CTAS(dspi->cs) |
+ SPI_PUSHR_CONT;
+
+ dspi->len -= 2;
+ } else {
+ if (!(dspi->dataflags & TRAN_STATE_TX_VOID)) {
+
+ d8 = *(u8 *)dspi->tx;
+ dspi->tx++;
+ } else {
+ d8 = (u8)dspi->void_write_data;
+ }
+
+ dspi_pushr = SPI_PUSHR_TXDATA(d8) |
+ SPI_PUSHR_PCS(dspi->cs) |
+ SPI_PUSHR_CTAS(dspi->cs) |
+ SPI_PUSHR_CONT;
+
+ dspi->len--;
+ }
+
+ if (dspi->len == 0 || tx_count == DSPI_FIFO_SIZE - 1) {
+ /* last transfer in the transfer */
+ dspi_pushr |= SPI_PUSHR_EOQ;
+ } else if (tx_word && (dspi->len == 1))
+ dspi_pushr |= SPI_PUSHR_EOQ;
+
+ if (first) {
+ first = 0;
+ dspi_pushr |= SPI_PUSHR_CTCNT; /* clear counter */
+ }
+
+ writel(dspi_pushr, dspi->base + SPI_PUSHR);
+ tx_count++;
+ }
+
+ return tx_count * (tx_word + 1);
+}
+
+static int dspi_transfer_read(struct fsl_dspi *dspi)
+{
+ int rx_count = 0;
+ int rx_word = is_double_byte_mode(dspi);
+ u16 d;
+ while ((dspi->rx < dspi->rx_end)
+ && (rx_count < DSPI_FIFO_SIZE)) {
+ if (rx_word) {
+ if ((dspi->rx_end - dspi->rx) == 1)
+ break;
+
+ d = SPI_POPR_RXDATA(readl(dspi->base + SPI_POPR));
+
+ if (!(dspi->dataflags & TRAN_STATE_RX_VOID))
+ *(u16 *)dspi->rx = d;
+ dspi->rx += 2;
+
+ } else {
+ d = SPI_POPR_RXDATA(readl(dspi->base + SPI_POPR));
+ if (!(dspi->dataflags & TRAN_STATE_RX_VOID))
+ *(u8 *)dspi->rx = d;
+ dspi->rx++;
+ }
+ rx_count++;
+ }
+
+ return rx_count;
+}
+
+static int dspi_txrx_transfer(struct spi_device *spi, struct spi_transfer *t)
+{
+ struct fsl_dspi *dspi = spi_master_get_devdata(spi->master);
+ dspi->cur_transfer = t;
+ dspi->cur_chip = spi_get_ctldata(spi);
+ dspi->cs = spi->chip_select;
+ dspi->void_write_data = dspi->cur_chip->void_write_data;
+
+ dspi->dataflags = 0;
+ dspi->tx = (void *)t->tx_buf;
+ dspi->tx_end = dspi->tx + t->len;
+ dspi->rx = t->rx_buf;
+ dspi->rx_end = dspi->rx + t->len;
+ dspi->len = t->len;
+
+ if (!dspi->rx)
+ dspi->dataflags |= TRAN_STATE_RX_VOID;
+
+ if (!dspi->tx)
+ dspi->dataflags |= TRAN_STATE_TX_VOID;
+
+ writel(dspi->cur_chip->mcr_val, dspi->base + SPI_MCR);
+ writel(dspi->cur_chip->ctar_val, dspi->base + SPI_CTAR(dspi->cs));
+ writel(SPI_RSER_EOQFE, dspi->base + SPI_RSER);
+
+ if (t->speed_hz)
+ writel(dspi->cur_chip->ctar_val,
+ dspi->base + SPI_CTAR(dspi->cs));
+
+ dspi_transfer_write(dspi);
+
+ if (wait_event_interruptible(dspi->waitq, dspi->waitflags))
+ dev_err(&dspi->pdev->dev, "wait transfer complete fail!\n");
+ dspi->waitflags = 0;
+
+ return t->len - dspi->len;
+}
+
+static void dspi_chipselect(struct spi_device *spi, int value)
+{
+ struct fsl_dspi *dspi = spi_master_get_devdata(spi->master);
+ u32 pushr = readl(dspi->base + SPI_PUSHR);
+
+ switch (value) {
+ case BITBANG_CS_ACTIVE:
+ pushr |= SPI_PUSHR_CONT;
+ case BITBANG_CS_INACTIVE:
+ pushr &= ~SPI_PUSHR_CONT;
+ }
+
+ writel(pushr, dspi->base + SPI_PUSHR);
+}
+
+static int dspi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
+{
+ struct chip_data *chip;
+ struct fsl_dspi *dspi = spi_master_get_devdata(spi->master);
+ unsigned char br = 0, pbr = 0, fmsz = 0;
+
+ /* Only alloc on first setup */
+ chip = spi_get_ctldata(spi);
+ if (chip == NULL) {
+ chip = kcalloc(1, sizeof(struct chip_data), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+ }
+
+ chip->mcr_val = SPI_MCR_MASTER | SPI_MCR_PCSIS |
+ SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF;
+ if ((spi->bits_per_word >= 4) && (spi->bits_per_word <= 16)) {
+ fmsz = spi->bits_per_word - 1;
+ } else {
+ pr_err("Invalid wordsize\n");
+ kfree(chip);
+ return -ENODEV;
+ }
+
+ chip->void_write_data = 0;
+
+ hz_to_spi_baud(&pbr, &br,
+ spi->max_speed_hz, clk_get_rate(dspi->clk));
+
+ chip->ctar_val = SPI_CTAR_FMSZ(fmsz)
+ | SPI_CTAR_CPOL(spi->mode & SPI_CPOL ? 1 : 0)
+ | SPI_CTAR_CPHA(spi->mode & SPI_CPHA ? 1 : 0)
+ | SPI_CTAR_LSBFE(spi->mode & SPI_LSB_FIRST ? 1 : 0)
+ | SPI_CTAR_PBR(pbr)
+ | SPI_CTAR_BR(br);
+
+ spi_set_ctldata(spi, chip);
+
+ return 0;
+}
+
+static int dspi_setup(struct spi_device *spi)
+{
+ if (!spi->max_speed_hz)
+ return -EINVAL;
+
+ if (!spi->bits_per_word)
+ spi->bits_per_word = 8;
+
+ return dspi_setup_transfer(spi, NULL);
+}
+
+static irqreturn_t dspi_interrupt(int irq, void *dev_id)
+{
+ struct fsl_dspi *dspi = (struct fsl_dspi *)dev_id;
+
+ writel(SPI_SR_EOQF, dspi->base + SPI_SR);
+
+ dspi_transfer_read(dspi);
+
+ if (!dspi->len) {
+ if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM)
+ set_bit_mode(dspi, 16);
+ dspi->waitflags = 1;
+ wake_up_interruptible(&dspi->waitq);
+ } else {
+ dspi_transfer_write(dspi);
+
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static struct of_device_id fsl_dspi_dt_ids[] = {
+ { .compatible = "fsl,vf610-dspi", .data = NULL, },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, fsl_dspi_dt_ids);
+
+#ifdef CONFIG_PM_SLEEP
+static int dspi_suspend(struct device *dev)
+{
+ struct spi_master *master = dev_get_drvdata(dev);
+ struct fsl_dspi *dspi = spi_master_get_devdata(master);
+
+ spi_master_suspend(master);
+ clk_disable_unprepare(dspi->clk);
+
+ return 0;
+}
+
+static int dspi_resume(struct device *dev)
+{
+
+ struct spi_master *master = dev_get_drvdata(dev);
+ struct fsl_dspi *dspi = spi_master_get_devdata(master);
+
+ clk_prepare_enable(dspi->clk);
+ spi_master_resume(master);
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops dspi_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(dspi_suspend, dspi_resume)
+};
+
+static int dspi_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct spi_master *master;
+ struct fsl_dspi *dspi;
+ struct resource *res;
+ int ret = 0, cs_num, bus_num;
+
+ master = spi_alloc_master(&pdev->dev, sizeof(struct fsl_dspi));
+ if (!master)
+ return -ENOMEM;
+
+ dspi = spi_master_get_devdata(master);
+ dspi->pdev = pdev;
+ dspi->bitbang.master = spi_master_get(master);
+ dspi->bitbang.chipselect = dspi_chipselect;
+ dspi->bitbang.setup_transfer = dspi_setup_transfer;
+ dspi->bitbang.txrx_bufs = dspi_txrx_transfer;
+ dspi->bitbang.master->setup = dspi_setup;
+ dspi->bitbang.master->dev.of_node = pdev->dev.of_node;
+
+ master->mode_bits = SPI_CPOL | SPI_CPHA;
+ master->bits_per_word_mask = SPI_BPW_MASK(4) | SPI_BPW_MASK(8) |
+ SPI_BPW_MASK(16);
+
+ ret = of_property_read_u32(np, "spi-num-chipselects", &cs_num);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "can't get spi-num-chipselects\n");
+ goto out_master_put;
+ }
+ master->num_chipselect = cs_num;
+
+ ret = of_property_read_u32(np, "bus-num", &bus_num);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "can't get bus-num\n");
+ goto out_master_put;
+ }
+ master->bus_num = bus_num;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "can't get platform resource\n");
+ ret = -EINVAL;
+ goto out_master_put;
+ }
+
+ dspi->base = devm_ioremap_resource(&pdev->dev, res);
+ if (!dspi->base) {
+ ret = -EINVAL;
+ goto out_master_put;
+ }
+
+ dspi->irq = platform_get_irq(pdev, 0);
+ if (dspi->irq < 0) {
+ dev_err(&pdev->dev, "can't get platform irq\n");
+ ret = dspi->irq;
+ goto out_master_put;
+ }
+
+ ret = devm_request_irq(&pdev->dev, dspi->irq, dspi_interrupt, 0,
+ pdev->name, dspi);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Unable to attach DSPI interrupt\n");
+ goto out_master_put;
+ }
+
+ dspi->clk = devm_clk_get(&pdev->dev, "dspi");
+ if (IS_ERR(dspi->clk)) {
+ ret = PTR_ERR(dspi->clk);
+ dev_err(&pdev->dev, "unable to get clock\n");
+ goto out_master_put;
+ }
+ clk_prepare_enable(dspi->clk);
+
+ init_waitqueue_head(&dspi->waitq);
+ platform_set_drvdata(pdev, dspi);
+
+ ret = spi_bitbang_start(&dspi->bitbang);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Problem registering DSPI master\n");
+ goto out_clk_put;
+ }
+
+ pr_info(KERN_INFO "Freescale DSPI master initialized\n");
+ return ret;
+
+out_clk_put:
+ clk_disable_unprepare(dspi->clk);
+out_master_put:
+ spi_master_put(master);
+ platform_set_drvdata(pdev, NULL);
+
+ return ret;
+}
+
+static int dspi_remove(struct platform_device *pdev)
+{
+ struct fsl_dspi *dspi = platform_get_drvdata(pdev);
+
+ /* Disconnect from the SPI framework */
+ spi_bitbang_stop(&dspi->bitbang);
+ spi_master_put(dspi->bitbang.master);
+
+ return 0;
+}
+
+static struct platform_driver fsl_dspi_driver = {
+ .driver.name = DRIVER_NAME,
+ .driver.of_match_table = fsl_dspi_dt_ids,
+ .driver.owner = THIS_MODULE,
+ .driver.pm = &dspi_pm,
+ .probe = dspi_probe,
+ .remove = dspi_remove,
+};
+module_platform_driver(fsl_dspi_driver);
+
+MODULE_DESCRIPTION("Freescale DSPI Controller Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
static struct spi_master * fsl_espi_probe(struct device *dev,
struct resource *mem, unsigned int irq)
{
- struct fsl_spi_platform_data *pdata = dev->platform_data;
+ struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
struct spi_master *master;
struct mpc8xxx_spi *mpc8xxx_spi;
struct fsl_espi_reg *reg_base;
static int of_fsl_espi_get_chipselects(struct device *dev)
{
struct device_node *np = dev->of_node;
- struct fsl_spi_platform_data *pdata = dev->platform_data;
+ struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
const u32 *prop;
int len;
int mpc8xxx_spi_probe(struct device *dev, struct resource *mem,
unsigned int irq)
{
- struct fsl_spi_platform_data *pdata = dev->platform_data;
+ struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
struct spi_master *master;
struct mpc8xxx_spi *mpc8xxx_spi;
int ret = 0;
static void fsl_spi_grlib_probe(struct device *dev)
{
- struct fsl_spi_platform_data *pdata = dev->platform_data;
+ struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
struct spi_master *master = dev_get_drvdata(dev);
struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(master);
struct fsl_spi_reg *reg_base = mpc8xxx_spi->reg_base;
static struct spi_master * fsl_spi_probe(struct device *dev,
struct resource *mem, unsigned int irq)
{
- struct fsl_spi_platform_data *pdata = dev->platform_data;
+ struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
struct spi_master *master;
struct mpc8xxx_spi *mpc8xxx_spi;
struct fsl_spi_reg *reg_base;
static void fsl_spi_cs_control(struct spi_device *spi, bool on)
{
struct device *dev = spi->dev.parent->parent;
- struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(dev->platform_data);
+ struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
+ struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata);
u16 cs = spi->chip_select;
int gpio = pinfo->gpios[cs];
bool alow = pinfo->alow_flags[cs];
static int of_fsl_spi_get_chipselects(struct device *dev)
{
struct device_node *np = dev->of_node;
- struct fsl_spi_platform_data *pdata = dev->platform_data;
+ struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata);
int ngpios;
int i = 0;
static int of_fsl_spi_free_chipselects(struct device *dev)
{
- struct fsl_spi_platform_data *pdata = dev->platform_data;
+ struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata);
int i;
int irq;
struct spi_master *master;
- if (!pdev->dev.platform_data)
+ if (!dev_get_platdata(&pdev->dev))
return -EINVAL;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (status > 0)
use_of = 1;
- pdata = pdev->dev.platform_data;
+ pdata = dev_get_platdata(&pdev->dev);
#ifdef GENERIC_BITBANG
if (!pdata || !pdata->num_chipselect)
return -ENODEV;
int status;
spi_gpio = platform_get_drvdata(pdev);
- pdata = pdev->dev.platform_data;
+ pdata = dev_get_platdata(&pdev->dev);
/* stop() unregisters child devices too */
status = spi_bitbang_stop(&spi_gpio->bitbang);
{ .compatible = "fsl,imx51-ecspi", .data = &imx51_ecspi_devtype_data, },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, spi_imx_dt_ids);
static void spi_imx_chipselect(struct spi_device *spi, int is_active)
{
if (!gpio_is_valid(cs_gpio))
continue;
- ret = gpio_request(spi_imx->chipselect[i], DRIVER_NAME);
+ ret = devm_gpio_request(&pdev->dev, spi_imx->chipselect[i],
+ DRIVER_NAME);
if (ret) {
dev_err(&pdev->dev, "can't get cs gpios\n");
- goto out_gpio_free;
+ goto out_master_put;
}
}
(struct spi_imx_devtype_data *) pdev->id_entry->driver_data;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "can't get platform resource\n");
- ret = -ENOMEM;
- goto out_gpio_free;
- }
-
- if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
- dev_err(&pdev->dev, "request_mem_region failed\n");
- ret = -EBUSY;
- goto out_gpio_free;
- }
-
- spi_imx->base = ioremap(res->start, resource_size(res));
- if (!spi_imx->base) {
- ret = -EINVAL;
- goto out_release_mem;
+ spi_imx->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(spi_imx->base)) {
+ ret = PTR_ERR(spi_imx->base);
+ goto out_master_put;
}
spi_imx->irq = platform_get_irq(pdev, 0);
if (spi_imx->irq < 0) {
ret = -EINVAL;
- goto out_iounmap;
+ goto out_master_put;
}
- ret = request_irq(spi_imx->irq, spi_imx_isr, 0, DRIVER_NAME, spi_imx);
+ ret = devm_request_irq(&pdev->dev, spi_imx->irq, spi_imx_isr, 0,
+ DRIVER_NAME, spi_imx);
if (ret) {
dev_err(&pdev->dev, "can't get irq%d: %d\n", spi_imx->irq, ret);
- goto out_iounmap;
+ goto out_master_put;
}
spi_imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(spi_imx->clk_ipg)) {
ret = PTR_ERR(spi_imx->clk_ipg);
- goto out_free_irq;
+ goto out_master_put;
}
spi_imx->clk_per = devm_clk_get(&pdev->dev, "per");
if (IS_ERR(spi_imx->clk_per)) {
ret = PTR_ERR(spi_imx->clk_per);
- goto out_free_irq;
+ goto out_master_put;
}
- clk_prepare_enable(spi_imx->clk_per);
- clk_prepare_enable(spi_imx->clk_ipg);
+ ret = clk_prepare_enable(spi_imx->clk_per);
+ if (ret)
+ goto out_master_put;
+
+ ret = clk_prepare_enable(spi_imx->clk_ipg);
+ if (ret)
+ goto out_put_per;
spi_imx->spi_clk = clk_get_rate(spi_imx->clk_per);
return ret;
out_clk_put:
- clk_disable_unprepare(spi_imx->clk_per);
clk_disable_unprepare(spi_imx->clk_ipg);
-out_free_irq:
- free_irq(spi_imx->irq, spi_imx);
-out_iounmap:
- iounmap(spi_imx->base);
-out_release_mem:
- release_mem_region(res->start, resource_size(res));
-out_gpio_free:
- while (--i >= 0) {
- if (gpio_is_valid(spi_imx->chipselect[i]))
- gpio_free(spi_imx->chipselect[i]);
- }
+out_put_per:
+ clk_disable_unprepare(spi_imx->clk_per);
+out_master_put:
spi_master_put(master);
- kfree(master);
+
return ret;
}
static int spi_imx_remove(struct platform_device *pdev)
{
struct spi_master *master = platform_get_drvdata(pdev);
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
struct spi_imx_data *spi_imx = spi_master_get_devdata(master);
- int i;
spi_bitbang_stop(&spi_imx->bitbang);
writel(0, spi_imx->base + MXC_CSPICTRL);
- clk_disable_unprepare(spi_imx->clk_per);
clk_disable_unprepare(spi_imx->clk_ipg);
- free_irq(spi_imx->irq, spi_imx);
- iounmap(spi_imx->base);
-
- for (i = 0; i < master->num_chipselect; i++)
- if (gpio_is_valid(spi_imx->chipselect[i]))
- gpio_free(spi_imx->chipselect[i]);
-
+ clk_disable_unprepare(spi_imx->clk_per);
spi_master_put(master);
- release_mem_region(res->start, resource_size(res));
-
return 0;
}
struct mpc512x_psc_fifo __iomem *fifo;
unsigned int irq;
u8 bits_per_word;
- u32 mclk;
+ struct clk *clk_mclk;
+ u32 mclk_rate;
struct completion txisrdone;
};
struct mpc52xx_psc __iomem *psc = mps->psc;
u32 sicr;
u32 ccr;
+ int speed;
u16 bclkdiv;
sicr = in_be32(&psc->sicr);
ccr = in_be32(&psc->ccr);
ccr &= 0xFF000000;
- if (cs->speed_hz)
- bclkdiv = (mps->mclk / cs->speed_hz) - 1;
- else
- bclkdiv = (mps->mclk / 1000000) - 1; /* default 1MHz */
+ speed = cs->speed_hz;
+ if (!speed)
+ speed = 1000000; /* default 1MHz */
+ bclkdiv = (mps->mclk_rate / speed) - 1;
ccr |= (((bclkdiv & 0xff) << 16) | (((bclkdiv >> 8) & 0xff) << 8));
out_be32(&psc->ccr, ccr);
{
struct mpc52xx_psc __iomem *psc = mps->psc;
struct mpc512x_psc_fifo __iomem *fifo = mps->fifo;
- struct clk *spiclk;
- int ret = 0;
- char name[32];
u32 sicr;
u32 ccr;
+ int speed;
u16 bclkdiv;
- sprintf(name, "psc%d_mclk", master->bus_num);
- spiclk = clk_get(&master->dev, name);
- clk_enable(spiclk);
- mps->mclk = clk_get_rate(spiclk);
- clk_put(spiclk);
-
/* Reset the PSC into a known state */
out_8(&psc->command, MPC52xx_PSC_RST_RX);
out_8(&psc->command, MPC52xx_PSC_RST_TX);
ccr = in_be32(&psc->ccr);
ccr &= 0xFF000000;
- bclkdiv = (mps->mclk / 1000000) - 1; /* default 1MHz */
+ speed = 1000000; /* default 1MHz */
+ bclkdiv = (mps->mclk_rate / speed) - 1;
ccr |= (((bclkdiv & 0xff) << 16) | (((bclkdiv >> 8) & 0xff) << 8));
out_be32(&psc->ccr, ccr);
mps->bits_per_word = 8;
- return ret;
+ return 0;
}
static irqreturn_t mpc512x_psc_spi_isr(int irq, void *dev_id)
u32 size, unsigned int irq,
s16 bus_num)
{
- struct fsl_spi_platform_data *pdata = dev->platform_data;
+ struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
struct mpc512x_psc_spi *mps;
struct spi_master *master;
int ret;
void *tempp;
+ int psc_num;
+ char clk_name[16];
+ struct clk *clk;
master = spi_alloc_master(dev, sizeof *mps);
if (master == NULL)
goto free_master;
init_completion(&mps->txisrdone);
+ psc_num = master->bus_num;
+ snprintf(clk_name, sizeof(clk_name), "psc%d_mclk", psc_num);
+ clk = devm_clk_get(dev, clk_name);
+ if (IS_ERR(clk))
+ goto free_irq;
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ goto free_irq;
+ mps->clk_mclk = clk;
+ mps->mclk_rate = clk_get_rate(clk);
+
ret = mpc512x_psc_spi_port_config(master, mps);
if (ret < 0)
- goto free_irq;
+ goto free_clock;
ret = spi_register_master(master);
if (ret < 0)
- goto free_irq;
+ goto free_clock;
return ret;
+free_clock:
+ clk_disable_unprepare(mps->clk_mclk);
free_irq:
free_irq(mps->irq, mps);
free_master:
struct mpc512x_psc_spi *mps = spi_master_get_devdata(master);
spi_unregister_master(master);
+ clk_disable_unprepare(mps->clk_mclk);
free_irq(mps->irq, mps);
if (mps->psc)
iounmap(mps->psc);
static int mpc52xx_psc_spi_do_probe(struct device *dev, u32 regaddr,
u32 size, unsigned int irq, s16 bus_num)
{
- struct fsl_spi_platform_data *pdata = dev->platform_data;
+ struct fsl_spi_platform_data *pdata = dev_get_platdata(dev);
struct mpc52xx_psc_spi *mps;
struct spi_master *master;
int ret;
{
struct mxs_spi *spi = spi_master_get_devdata(dev->master);
struct mxs_ssp *ssp = &spi->ssp;
- uint8_t bits_per_word;
uint32_t hz = 0;
- bits_per_word = dev->bits_per_word;
- if (t && t->bits_per_word)
- bits_per_word = t->bits_per_word;
-
hz = dev->max_speed_hz;
if (t && t->speed_hz)
hz = min(hz, t->speed_hz);
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq_err = platform_get_irq(pdev, 0);
- if (!iores || irq_err < 0)
+ if (irq_err < 0)
return -EINVAL;
base = devm_ioremap_resource(&pdev->dev, iores);
goto out_master_free;
}
- clk_prepare_enable(ssp->clk);
+ ret = clk_prepare_enable(ssp->clk);
+ if (ret)
+ goto out_dma_release;
+
clk_set_rate(ssp->clk, clk_freq);
ssp->clk_rate = clk_get_rate(ssp->clk) / 1000;
- stmp_reset_block(ssp->base);
+ ret = stmp_reset_block(ssp->base);
+ if (ret)
+ goto out_disable_clk;
platform_set_drvdata(pdev, master);
ret = spi_register_master(master);
if (ret) {
dev_err(&pdev->dev, "Cannot register SPI master, %d\n", ret);
- goto out_free_dma;
+ goto out_disable_clk;
}
return 0;
-out_free_dma:
- dma_release_channel(ssp->dmach);
+out_disable_clk:
clk_disable_unprepare(ssp->clk);
+out_dma_release:
+ dma_release_channel(ssp->dmach);
out_master_free:
spi_master_put(master);
return ret;
ssp = &spi->ssp;
spi_unregister_master(master);
-
- dma_release_channel(ssp->dmach);
-
clk_disable_unprepare(ssp->clk);
-
+ dma_release_channel(ssp->dmach);
spi_master_put(master);
return 0;
spin_unlock_irqrestore(&hw->lock, flags);
}
-static int nuc900_spi_setupxfer(struct spi_device *spi,
- struct spi_transfer *t)
-{
- return 0;
-}
-
-static int nuc900_spi_setup(struct spi_device *spi)
-{
- return 0;
-}
-
static inline unsigned int hw_txbyte(struct nuc900_spi *hw, int count)
{
return hw->tx ? hw->tx[count] : 0;
hw = spi_master_get_devdata(master);
hw->master = spi_master_get(master);
- hw->pdata = pdev->dev.platform_data;
+ hw->pdata = dev_get_platdata(&pdev->dev);
hw->dev = &pdev->dev;
if (hw->pdata == NULL) {
platform_set_drvdata(pdev, hw);
init_completion(&hw->done);
- master->mode_bits = SPI_MODE_0;
+ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
master->num_chipselect = hw->pdata->num_cs;
master->bus_num = hw->pdata->bus_num;
hw->bitbang.master = hw->master;
- hw->bitbang.setup_transfer = nuc900_spi_setupxfer;
hw->bitbang.chipselect = nuc900_spi_chipsel;
hw->bitbang.txrx_bufs = nuc900_spi_txrx;
- hw->bitbang.master->setup = nuc900_spi_setup;
hw->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (hw->res == NULL) {
static int tiny_spi_probe(struct platform_device *pdev)
{
- struct tiny_spi_platform_data *platp = pdev->dev.platform_data;
+ struct tiny_spi_platform_data *platp = dev_get_platdata(&pdev->dev);
struct tiny_spi *hw;
struct spi_master *master;
struct resource *res;
/* find and map our resources */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- goto exit_busy;
- if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
- pdev->name))
- goto exit_busy;
- hw->base = devm_ioremap_nocache(&pdev->dev, res->start,
- resource_size(res));
- if (!hw->base)
- goto exit_busy;
+ hw->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(hw->base)) {
+ err = PTR_ERR(hw->base);
+ goto exit;
+ }
/* irq is optional */
hw->irq = platform_get_irq(pdev, 0);
if (hw->irq >= 0) {
if (platp) {
hw->gpio_cs_count = platp->gpio_cs_count;
hw->gpio_cs = platp->gpio_cs;
- if (platp->gpio_cs_count && !platp->gpio_cs)
- goto exit_busy;
+ if (platp->gpio_cs_count && !platp->gpio_cs) {
+ err = -EBUSY;
+ goto exit;
+ }
hw->freq = platp->freq;
hw->baudwidth = platp->baudwidth;
} else {
exit_gpio:
while (i-- > 0)
gpio_free(hw->gpio_cs[i]);
-exit_busy:
- err = -EBUSY;
exit:
spi_master_put(master);
return err;
#define OCTEON_SPI_MAX_CLOCK_HZ 16000000
struct octeon_spi {
- struct spi_master *my_master;
u64 register_base;
u64 last_cfg;
u64 cs_enax;
unsigned int speed_hz;
int mode;
bool cpha, cpol;
- int bits_per_word;
const u8 *tx_buf;
u8 *rx_buf;
int len;
mode = msg_setup->mode;
cpha = mode & SPI_CPHA;
cpol = mode & SPI_CPOL;
- bits_per_word = msg_setup->bits_per_word;
if (xfer->speed_hz)
speed_hz = xfer->speed_hz;
- if (xfer->bits_per_word)
- bits_per_word = xfer->bits_per_word;
if (speed_hz > OCTEON_SPI_MAX_CLOCK_HZ)
speed_hz = OCTEON_SPI_MAX_CLOCK_HZ;
return xfer->len;
}
-static int octeon_spi_validate_bpw(struct spi_device *spi, u32 speed)
-{
- switch (speed) {
- case 8:
- break;
- default:
- dev_err(&spi->dev, "Error: %d bits per word not supported\n",
- speed);
- return -EINVAL;
- }
- return 0;
-}
-
static int octeon_spi_transfer_one_message(struct spi_master *master,
struct spi_message *msg)
{
goto err;
}
- list_for_each_entry(xfer, &msg->transfers, transfer_list) {
- if (xfer->bits_per_word) {
- status = octeon_spi_validate_bpw(msg->spi,
- xfer->bits_per_word);
- if (status)
- goto err;
- }
- }
-
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
bool last_xfer = &xfer->transfer_list == msg->transfers.prev;
int r = octeon_spi_do_transfer(p, msg, xfer, last_xfer);
static int octeon_spi_setup(struct spi_device *spi)
{
- int r;
struct octeon_spi_setup *new_setup;
struct octeon_spi_setup *old_setup = spi_get_ctldata(spi);
- r = octeon_spi_validate_bpw(spi, spi->bits_per_word);
- if (r)
- return r;
-
new_setup = octeon_spi_new_setup(spi);
if (!new_setup)
return -ENOMEM;
kfree(old_setup);
}
-static int octeon_spi_nop_transfer_hardware(struct spi_master *master)
-{
- return 0;
-}
-
static int octeon_spi_probe(struct platform_device *pdev)
{
-
struct resource *res_mem;
struct spi_master *master;
struct octeon_spi *p;
if (!master)
return -ENOMEM;
p = spi_master_get_devdata(master);
- platform_set_drvdata(pdev, p);
- p->my_master = master;
+ platform_set_drvdata(pdev, master);
res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
master->setup = octeon_spi_setup;
master->cleanup = octeon_spi_cleanup;
- master->prepare_transfer_hardware = octeon_spi_nop_transfer_hardware;
master->transfer_one_message = octeon_spi_transfer_one_message;
- master->unprepare_transfer_hardware = octeon_spi_nop_transfer_hardware;
+ master->bits_per_word_mask = SPI_BPW_MASK(8);
master->dev.of_node = pdev->dev.of_node;
err = spi_register_master(master);
static int octeon_spi_remove(struct platform_device *pdev)
{
- struct octeon_spi *p = platform_get_drvdata(pdev);
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct octeon_spi *p = spi_master_get_devdata(master);
u64 register_base = p->register_base;
- spi_unregister_master(p->my_master);
+ spi_unregister_master(master);
/* Clear the CSENA* and put everything in a known state. */
cvmx_write_csr(register_base + OCTEON_SPI_CFG, 0);
#define SPI_SHUTDOWN 1
struct omap1_spi100k {
- struct work_struct work;
-
- /* lock protects queue and registers */
- spinlock_t lock;
- struct list_head msg_queue;
struct spi_master *master;
struct clk *ick;
struct clk *fck;
int word_len;
};
-static struct workqueue_struct *omap1_spi100k_wq;
-
#define MOD_REG_BIT(val, mask, set) do { \
if (set) \
val |= mask; \
spi100k_open(spi->master);
- clk_enable(spi100k->ick);
- clk_enable(spi100k->fck);
+ clk_prepare_enable(spi100k->ick);
+ clk_prepare_enable(spi100k->fck);
ret = omap1_spi100k_setup_transfer(spi, NULL);
- clk_disable(spi100k->ick);
- clk_disable(spi100k->fck);
+ clk_disable_unprepare(spi100k->ick);
+ clk_disable_unprepare(spi100k->fck);
return ret;
}
-static void omap1_spi100k_work(struct work_struct *work)
+static int omap1_spi100k_prepare_hardware(struct spi_master *master)
{
- struct omap1_spi100k *spi100k;
- int status = 0;
+ struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
- spi100k = container_of(work, struct omap1_spi100k, work);
- spin_lock_irq(&spi100k->lock);
+ clk_prepare_enable(spi100k->ick);
+ clk_prepare_enable(spi100k->fck);
- clk_enable(spi100k->ick);
- clk_enable(spi100k->fck);
+ return 0;
+}
- /* We only enable one channel at a time -- the one whose message is
- * at the head of the queue -- although this controller would gladly
- * arbitrate among multiple channels. This corresponds to "single
- * channel" master mode. As a side effect, we need to manage the
- * chipselect with the FORCE bit ... CS != channel enable.
- */
- while (!list_empty(&spi100k->msg_queue)) {
- struct spi_message *m;
- struct spi_device *spi;
- struct spi_transfer *t = NULL;
- int cs_active = 0;
- struct omap1_spi100k_cs *cs;
- int par_override = 0;
-
- m = container_of(spi100k->msg_queue.next, struct spi_message,
- queue);
-
- list_del_init(&m->queue);
- spin_unlock_irq(&spi100k->lock);
-
- spi = m->spi;
- cs = spi->controller_state;
-
- list_for_each_entry(t, &m->transfers, transfer_list) {
- if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) {
- status = -EINVAL;
+static int omap1_spi100k_transfer_one_message(struct spi_master *master,
+ struct spi_message *m)
+{
+ struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
+ struct spi_device *spi = m->spi;
+ struct spi_transfer *t = NULL;
+ int cs_active = 0;
+ int par_override = 0;
+ int status = 0;
+
+ list_for_each_entry(t, &m->transfers, transfer_list) {
+ if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) {
+ status = -EINVAL;
+ break;
+ }
+ if (par_override || t->speed_hz || t->bits_per_word) {
+ par_override = 1;
+ status = omap1_spi100k_setup_transfer(spi, t);
+ if (status < 0)
break;
- }
- if (par_override || t->speed_hz || t->bits_per_word) {
- par_override = 1;
- status = omap1_spi100k_setup_transfer(spi, t);
- if (status < 0)
- break;
- if (!t->speed_hz && !t->bits_per_word)
- par_override = 0;
- }
+ if (!t->speed_hz && !t->bits_per_word)
+ par_override = 0;
+ }
- if (!cs_active) {
- omap1_spi100k_force_cs(spi100k, 1);
- cs_active = 1;
- }
+ if (!cs_active) {
+ omap1_spi100k_force_cs(spi100k, 1);
+ cs_active = 1;
+ }
- if (t->len) {
- unsigned count;
+ if (t->len) {
+ unsigned count;
- count = omap1_spi100k_txrx_pio(spi, t);
- m->actual_length += count;
+ count = omap1_spi100k_txrx_pio(spi, t);
+ m->actual_length += count;
- if (count != t->len) {
- status = -EIO;
- break;
- }
+ if (count != t->len) {
+ status = -EIO;
+ break;
}
+ }
- if (t->delay_usecs)
- udelay(t->delay_usecs);
+ if (t->delay_usecs)
+ udelay(t->delay_usecs);
- /* ignore the "leave it on after last xfer" hint */
+ /* ignore the "leave it on after last xfer" hint */
- if (t->cs_change) {
- omap1_spi100k_force_cs(spi100k, 0);
- cs_active = 0;
- }
- }
-
- /* Restore defaults if they were overriden */
- if (par_override) {
- par_override = 0;
- status = omap1_spi100k_setup_transfer(spi, NULL);
+ if (t->cs_change) {
+ omap1_spi100k_force_cs(spi100k, 0);
+ cs_active = 0;
}
+ }
- if (cs_active)
- omap1_spi100k_force_cs(spi100k, 0);
+ /* Restore defaults if they were overriden */
+ if (par_override) {
+ par_override = 0;
+ status = omap1_spi100k_setup_transfer(spi, NULL);
+ }
- m->status = status;
- m->complete(m->context);
+ if (cs_active)
+ omap1_spi100k_force_cs(spi100k, 0);
- spin_lock_irq(&spi100k->lock);
- }
+ m->status = status;
- clk_disable(spi100k->ick);
- clk_disable(spi100k->fck);
- spin_unlock_irq(&spi100k->lock);
+ spi_finalize_current_message(master);
- if (status < 0)
- printk(KERN_WARNING "spi transfer failed with %d\n", status);
+ return status;
}
-static int omap1_spi100k_transfer(struct spi_device *spi, struct spi_message *m)
+static int omap1_spi100k_unprepare_hardware(struct spi_master *master)
{
- struct omap1_spi100k *spi100k;
- unsigned long flags;
- struct spi_transfer *t;
-
- m->actual_length = 0;
- m->status = -EINPROGRESS;
-
- spi100k = spi_master_get_devdata(spi->master);
-
- /* Don't accept new work if we're shutting down */
- if (spi100k->state == SPI_SHUTDOWN)
- return -ESHUTDOWN;
-
- /* reject invalid messages and transfers */
- if (list_empty(&m->transfers) || !m->complete)
- return -EINVAL;
-
- list_for_each_entry(t, &m->transfers, transfer_list) {
- const void *tx_buf = t->tx_buf;
- void *rx_buf = t->rx_buf;
- unsigned len = t->len;
-
- if (t->speed_hz > OMAP1_SPI100K_MAX_FREQ
- || (len && !(rx_buf || tx_buf))) {
- dev_dbg(&spi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n",
- t->speed_hz,
- len,
- tx_buf ? "tx" : "",
- rx_buf ? "rx" : "",
- t->bits_per_word);
- return -EINVAL;
- }
-
- if (t->speed_hz && t->speed_hz < OMAP1_SPI100K_MAX_FREQ/(1<<16)) {
- dev_dbg(&spi->dev, "%d Hz max exceeds %d\n",
- t->speed_hz,
- OMAP1_SPI100K_MAX_FREQ/(1<<16));
- return -EINVAL;
- }
-
- }
-
- spin_lock_irqsave(&spi100k->lock, flags);
- list_add_tail(&m->queue, &spi100k->msg_queue);
- queue_work(omap1_spi100k_wq, &spi100k->work);
- spin_unlock_irqrestore(&spi100k->lock, flags);
+ struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
- return 0;
-}
+ clk_disable_unprepare(spi100k->ick);
+ clk_disable_unprepare(spi100k->fck);
-static int omap1_spi100k_reset(struct omap1_spi100k *spi100k)
-{
return 0;
}
master->bus_num = pdev->id;
master->setup = omap1_spi100k_setup;
- master->transfer = omap1_spi100k_transfer;
+ master->transfer_one_message = omap1_spi100k_transfer_one_message;
+ master->prepare_transfer_hardware = omap1_spi100k_prepare_hardware;
+ master->unprepare_transfer_hardware = omap1_spi100k_unprepare_hardware;
master->cleanup = NULL;
master->num_chipselect = 2;
master->mode_bits = MODEBITS;
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
+ master->min_speed_hz = OMAP1_SPI100K_MAX_FREQ/(1<<16);
+ master->max_speed_hz = OMAP1_SPI100K_MAX_FREQ;
platform_set_drvdata(pdev, master);
* You should allocate this with ioremap() before initializing
* the SPI.
*/
- spi100k->base = (void __iomem *) pdev->dev.platform_data;
-
- INIT_WORK(&spi100k->work, omap1_spi100k_work);
+ spi100k->base = (void __iomem *)dev_get_platdata(&pdev->dev);
- spin_lock_init(&spi100k->lock);
- INIT_LIST_HEAD(&spi100k->msg_queue);
- spi100k->ick = clk_get(&pdev->dev, "ick");
+ spi100k->ick = devm_clk_get(&pdev->dev, "ick");
if (IS_ERR(spi100k->ick)) {
dev_dbg(&pdev->dev, "can't get spi100k_ick\n");
status = PTR_ERR(spi100k->ick);
- goto err1;
+ goto err;
}
- spi100k->fck = clk_get(&pdev->dev, "fck");
+ spi100k->fck = devm_clk_get(&pdev->dev, "fck");
if (IS_ERR(spi100k->fck)) {
dev_dbg(&pdev->dev, "can't get spi100k_fck\n");
status = PTR_ERR(spi100k->fck);
- goto err2;
+ goto err;
}
- if (omap1_spi100k_reset(spi100k) < 0)
- goto err3;
-
status = spi_register_master(master);
if (status < 0)
- goto err3;
+ goto err;
spi100k->state = SPI_RUNNING;
return status;
-err3:
- clk_put(spi100k->fck);
-err2:
- clk_put(spi100k->ick);
-err1:
+err:
spi_master_put(master);
return status;
}
struct spi_master *master;
struct omap1_spi100k *spi100k;
struct resource *r;
- unsigned limit = 500;
- unsigned long flags;
int status = 0;
master = platform_get_drvdata(pdev);
spi100k = spi_master_get_devdata(master);
- spin_lock_irqsave(&spi100k->lock, flags);
-
- spi100k->state = SPI_SHUTDOWN;
- while (!list_empty(&spi100k->msg_queue) && limit--) {
- spin_unlock_irqrestore(&spi100k->lock, flags);
- msleep(10);
- spin_lock_irqsave(&spi100k->lock, flags);
- }
-
- if (!list_empty(&spi100k->msg_queue))
- status = -EBUSY;
-
- spin_unlock_irqrestore(&spi100k->lock, flags);
-
if (status != 0)
return status;
- clk_put(spi100k->fck);
- clk_put(spi100k->ick);
-
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
spi_unregister_master(master);
.name = "omap1_spi100k",
.owner = THIS_MODULE,
},
+ .probe = omap1_spi100k_probe,
.remove = omap1_spi100k_remove,
};
-
-static int __init omap1_spi100k_init(void)
-{
- omap1_spi100k_wq = create_singlethread_workqueue(
- omap1_spi100k_driver.driver.name);
-
- if (omap1_spi100k_wq == NULL)
- return -1;
-
- return platform_driver_probe(&omap1_spi100k_driver, omap1_spi100k_probe);
-}
-
-static void __exit omap1_spi100k_exit(void)
-{
- platform_driver_unregister(&omap1_spi100k_driver);
-
- destroy_workqueue(omap1_spi100k_wq);
-}
-
-module_init(omap1_spi100k_init);
-module_exit(omap1_spi100k_exit);
+module_platform_driver(omap1_spi100k_driver);
MODULE_DESCRIPTION("OMAP7xx SPI 100k controller driver");
MODULE_AUTHOR("Fabrice Crohas <fcrohas@gmail.com>");
__raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0);
}
-static int omap2_prepare_transfer(struct spi_master *master)
-{
- struct omap2_mcspi *mcspi = spi_master_get_devdata(master);
-
- pm_runtime_get_sync(mcspi->dev);
- return 0;
-}
-
-static int omap2_unprepare_transfer(struct spi_master *master)
-{
- struct omap2_mcspi *mcspi = spi_master_get_devdata(master);
-
- pm_runtime_mark_last_busy(mcspi->dev);
- pm_runtime_put_autosuspend(mcspi->dev);
- return 0;
-}
-
static int mcspi_wait_for_reg_bit(void __iomem *reg, unsigned long bit)
{
unsigned long timeout;
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
master->setup = omap2_mcspi_setup;
- master->prepare_transfer_hardware = omap2_prepare_transfer;
- master->unprepare_transfer_hardware = omap2_unprepare_transfer;
+ master->auto_runtime_pm = true;
master->transfer_one_message = omap2_mcspi_transfer_one_message;
master->cleanup = omap2_mcspi_cleanup;
master->dev.of_node = node;
if (of_get_property(node, "ti,pindir-d0-out-d1-in", NULL))
mcspi->pin_dir = MCSPI_PINDIR_D0_OUT_D1_IN;
} else {
- pdata = pdev->dev.platform_data;
+ pdata = dev_get_platdata(&pdev->dev);
master->num_chipselect = pdata->num_cs;
if (pdev->id != -1)
master->bus_num = pdev->id;
#include <linux/module.h>
#include <linux/of.h>
#include <linux/clk.h>
+#include <linux/sizes.h>
#include <asm/unaligned.h>
#define DRIVER_NAME "orion_spi"
spi->min_speed = DIV_ROUND_UP(tclk_hz, 30);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (r == NULL) {
- status = -ENODEV;
+ spi->base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(spi->base)) {
+ status = PTR_ERR(spi->base);
goto out_rel_clk;
}
- if (!request_mem_region(r->start, resource_size(r),
- dev_name(&pdev->dev))) {
- status = -EBUSY;
- goto out_rel_clk;
- }
- spi->base = ioremap(r->start, SZ_1K);
-
if (orion_spi_reset(spi) < 0)
- goto out_rel_mem;
+ goto out_rel_clk;
master->dev.of_node = pdev->dev.of_node;
status = spi_register_master(master);
if (status < 0)
- goto out_rel_mem;
+ goto out_rel_clk;
return status;
-out_rel_mem:
- release_mem_region(r->start, resource_size(r));
out_rel_clk:
clk_disable_unprepare(spi->clk);
clk_put(spi->clk);
static int orion_spi_remove(struct platform_device *pdev)
{
struct spi_master *master;
- struct resource *r;
struct orion_spi *spi;
master = platform_get_drvdata(pdev);
clk_disable_unprepare(spi->clk);
clk_put(spi->clk);
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(r->start, resource_size(r));
-
spi_unregister_master(master);
return 0;
return 0;
}
-static int pl022_prepare_transfer_hardware(struct spi_master *master)
-{
- struct pl022 *pl022 = spi_master_get_devdata(master);
-
- /*
- * Just make sure we have all we need to run the transfer by syncing
- * with the runtime PM framework.
- */
- pm_runtime_get_sync(&pl022->adev->dev);
- return 0;
-}
-
static int pl022_unprepare_transfer_hardware(struct spi_master *master)
{
struct pl022 *pl022 = spi_master_get_devdata(master);
writew((readw(SSP_CR1(pl022->virtbase)) &
(~SSP_CR1_MASK_SSE)), SSP_CR1(pl022->virtbase));
- if (pl022->master_info->autosuspend_delay > 0) {
- pm_runtime_mark_last_busy(&pl022->adev->dev);
- pm_runtime_put_autosuspend(&pl022->adev->dev);
- } else {
- pm_runtime_put(&pl022->adev->dev);
- }
-
return 0;
}
static int pl022_probe(struct amba_device *adev, const struct amba_id *id)
{
struct device *dev = &adev->dev;
- struct pl022_ssp_controller *platform_info = adev->dev.platform_data;
+ struct pl022_ssp_controller *platform_info =
+ dev_get_platdata(&adev->dev);
struct spi_master *master;
struct pl022 *pl022 = NULL; /*Data for this driver */
struct device_node *np = adev->dev.of_node;
master->num_chipselect = num_cs;
master->cleanup = pl022_cleanup;
master->setup = pl022_setup;
- master->prepare_transfer_hardware = pl022_prepare_transfer_hardware;
+ master->auto_runtime_pm = true;
master->transfer_one_message = pl022_transfer_one_message;
master->unprepare_transfer_hardware = pl022_unprepare_transfer_hardware;
master->rt = platform_info->rt;
status = -ENOMEM;
goto err_no_ioremap;
}
- printk(KERN_INFO "pl022: mapped registers from 0x%08x to %p\n",
- adev->res.start, pl022->virtbase);
+ printk(KERN_INFO "pl022: mapped registers from %pa to %p\n",
+ &adev->res.start, pl022->virtbase);
pl022->clk = devm_clk_get(&adev->dev, NULL);
if (IS_ERR(pl022->clk)) {
#define LPSS_TX_HITHRESH_DFLT 224
/* Offset from drv_data->lpss_base */
+#define GENERAL_REG 0x08
+#define GENERAL_REG_RXTO_HOLDOFF_DISABLE BIT(24)
#define SSP_REG 0x0c
#define SPI_CS_CONTROL 0x18
#define SPI_CS_CONTROL_SW_MODE BIT(0)
__lpss_ssp_write_priv(drv_data, SPI_CS_CONTROL, value);
/* Enable multiblock DMA transfers */
- if (drv_data->master_info->enable_dma)
+ if (drv_data->master_info->enable_dma) {
__lpss_ssp_write_priv(drv_data, SSP_REG, 1);
+
+ value = __lpss_ssp_read_priv(drv_data, GENERAL_REG);
+ value |= GENERAL_REG_RXTO_HOLDOFF_DISABLE;
+ __lpss_ssp_write_priv(drv_data, GENERAL_REG, value);
+ }
}
static void lpss_ssp_cs_control(struct driver_data *drv_data, bool enable)
return 0;
}
-static int pxa2xx_spi_prepare_transfer(struct spi_master *master)
-{
- struct driver_data *drv_data = spi_master_get_devdata(master);
-
- pm_runtime_get_sync(&drv_data->pdev->dev);
- return 0;
-}
-
static int pxa2xx_spi_unprepare_transfer(struct spi_master *master)
{
struct driver_data *drv_data = spi_master_get_devdata(master);
write_SSCR0(read_SSCR0(drv_data->ioaddr) & ~SSCR0_SSE,
drv_data->ioaddr);
- pm_runtime_mark_last_busy(&drv_data->pdev->dev);
- pm_runtime_put_autosuspend(&drv_data->pdev->dev);
return 0;
}
master->cleanup = cleanup;
master->setup = setup;
master->transfer_one_message = pxa2xx_spi_transfer_one_message;
- master->prepare_transfer_hardware = pxa2xx_spi_prepare_transfer;
master->unprepare_transfer_hardware = pxa2xx_spi_unprepare_transfer;
+ master->auto_runtime_pm = true;
drv_data->ssp_type = ssp->type;
drv_data->null_dma_buf = (u32 *)PTR_ALIGN(&drv_data[1], DMA_ALIGNMENT);
unsigned long flags;
int ret;
- spin_lock_irqsave(&rspi->lock, flags);
- while (!list_empty(&rspi->queue)) {
+ while (1) {
+ spin_lock_irqsave(&rspi->lock, flags);
+ if (list_empty(&rspi->queue)) {
+ spin_unlock_irqrestore(&rspi->lock, flags);
+ break;
+ }
mesg = list_entry(rspi->queue.next, struct spi_message, queue);
list_del_init(&mesg->queue);
spin_unlock_irqrestore(&rspi->lock, flags);
mesg->status = 0;
mesg->complete(mesg->context);
-
- spin_lock_irqsave(&rspi->lock, flags);
}
return;
static int rspi_request_dma(struct rspi_data *rspi,
struct platform_device *pdev)
{
- struct rspi_plat_data *rspi_pd = pdev->dev.platform_data;
+ struct rspi_plat_data *rspi_pd = dev_get_platdata(&pdev->dev);
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
dma_cap_mask_t mask;
struct dma_slave_config cfg;
int ret;
- if (!rspi_pd)
+ if (!res || !rspi_pd)
return 0; /* The driver assumes no error. */
rspi->dma_width_16bit = rspi_pd->dma_width_16bit;
if (rspi->chan_rx) {
cfg.slave_id = rspi_pd->dma_rx_id;
cfg.direction = DMA_DEV_TO_MEM;
+ cfg.dst_addr = 0;
+ cfg.src_addr = res->start + RSPI_SPDR;
ret = dmaengine_slave_config(rspi->chan_rx, &cfg);
if (!ret)
dev_info(&pdev->dev, "Use DMA when rx.\n");
if (rspi->chan_tx) {
cfg.slave_id = rspi_pd->dma_tx_id;
cfg.direction = DMA_MEM_TO_DEV;
+ cfg.dst_addr = res->start + RSPI_SPDR;
+ cfg.src_addr = 0;
ret = dmaengine_slave_config(rspi->chan_tx, &cfg);
if (!ret)
dev_info(&pdev->dev, "Use DMA when tx\n");
static int rspi_remove(struct platform_device *pdev)
{
- struct rspi_data *rspi = platform_get_drvdata(pdev);
+ struct rspi_data *rspi = spi_master_get(platform_get_drvdata(pdev));
spi_unregister_master(rspi->master);
rspi_release_dma(rspi);
memset(hw, 0, sizeof(struct s3c24xx_spi));
hw->master = spi_master_get(master);
- hw->pdata = pdata = pdev->dev.platform_data;
+ hw->pdata = pdata = dev_get_platdata(&pdev->dev);
hw->dev = &pdev->dev;
if (pdata == NULL) {
static int s3c24xx_spi_suspend(struct device *dev)
{
- struct s3c24xx_spi *hw = platform_get_drvdata(to_platform_device(dev));
+ struct s3c24xx_spi *hw = dev_get_drvdata(dev);
if (hw->pdata && hw->pdata->gpio_setup)
hw->pdata->gpio_setup(hw->pdata, 0);
static int s3c24xx_spi_resume(struct device *dev)
{
- struct s3c24xx_spi *hw = platform_get_drvdata(to_platform_device(dev));
+ struct s3c24xx_spi *hw = dev_get_drvdata(dev);
s3c24xx_spi_initialsetup(hw);
return 0;
* @master: Pointer to the SPI Protocol master.
* @cntrlr_info: Platform specific data for the controller this driver manages.
* @tgl_spi: Pointer to the last CS left untoggled by the cs_change hint.
- * @queue: To log SPI xfer requests.
* @lock: Controller specific lock.
* @state: Set of FLAGS to indicate status.
* @rx_dmach: Controller's DMA channel for Rx.
struct spi_master *master;
struct s3c64xx_spi_info *cntrlr_info;
struct spi_device *tgl_spi;
- struct list_head queue;
spinlock_t lock;
unsigned long sfr_start;
struct completion xfer_completion;
req.cap = DMA_SLAVE;
req.client = &s3c64xx_spi_dma_client;
- sdd->rx_dma.ch = (void *)sdd->ops->request(sdd->rx_dma.dmach, &req, dev, "rx");
- sdd->tx_dma.ch = (void *)sdd->ops->request(sdd->tx_dma.dmach, &req, dev, "tx");
+ sdd->rx_dma.ch = (struct dma_chan *)(unsigned long)sdd->ops->request(
+ sdd->rx_dma.dmach, &req, dev, "rx");
+ sdd->tx_dma.ch = (struct dma_chan *)(unsigned long)sdd->ops->request(
+ sdd->tx_dma.dmach, &req, dev, "tx");
return 1;
}
while (!is_polling(sdd) && !acquire_dma(sdd))
usleep_range(10000, 11000);
- pm_runtime_get_sync(&sdd->pdev->dev);
-
return 0;
}
sdd->ops->release((enum dma_ch)sdd->tx_dma.ch,
&s3c64xx_spi_dma_client);
}
- pm_runtime_put(&sdd->pdev->dev);
return 0;
}
{
struct s3c64xx_spi_driver_data *sdd;
struct dma_slave_config config;
- struct scatterlist sg;
struct dma_async_tx_descriptor *desc;
+ memset(&config, 0, sizeof(config));
+
if (dma->direction == DMA_DEV_TO_MEM) {
sdd = container_of((void *)dma,
struct s3c64xx_spi_driver_data, rx_dma);
dmaengine_slave_config(dma->ch, &config);
}
- sg_init_table(&sg, 1);
- sg_dma_len(&sg) = len;
- sg_set_page(&sg, pfn_to_page(PFN_DOWN(buf)),
- len, offset_in_page(buf));
- sg_dma_address(&sg) = buf;
-
- desc = dmaengine_prep_slave_sg(dma->ch,
- &sg, 1, dma->direction, DMA_PREP_INTERRUPT);
+ desc = dmaengine_prep_slave_single(dma->ch, buf, len,
+ dma->direction, DMA_PREP_INTERRUPT);
desc->callback = s3c64xx_spi_dmacb;
desc->callback_param = dma;
dma_cap_mask_t mask;
int ret;
- if (is_polling(sdd))
- return 0;
-
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
-
- /* Acquire DMA channels */
- sdd->rx_dma.ch = dma_request_slave_channel_compat(mask, filter,
- (void*)sdd->rx_dma.dmach, dev, "rx");
- if (!sdd->rx_dma.ch) {
- dev_err(dev, "Failed to get RX DMA channel\n");
- ret = -EBUSY;
- goto out;
- }
+ if (!is_polling(sdd)) {
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ /* Acquire DMA channels */
+ sdd->rx_dma.ch = dma_request_slave_channel_compat(mask, filter,
+ (void *)sdd->rx_dma.dmach, dev, "rx");
+ if (!sdd->rx_dma.ch) {
+ dev_err(dev, "Failed to get RX DMA channel\n");
+ ret = -EBUSY;
+ goto out;
+ }
- sdd->tx_dma.ch = dma_request_slave_channel_compat(mask, filter,
- (void*)sdd->tx_dma.dmach, dev, "tx");
- if (!sdd->tx_dma.ch) {
- dev_err(dev, "Failed to get TX DMA channel\n");
- ret = -EBUSY;
- goto out_rx;
+ sdd->tx_dma.ch = dma_request_slave_channel_compat(mask, filter,
+ (void *)sdd->tx_dma.dmach, dev, "tx");
+ if (!sdd->tx_dma.ch) {
+ dev_err(dev, "Failed to get TX DMA channel\n");
+ ret = -EBUSY;
+ goto out_rx;
+ }
}
ret = pm_runtime_get_sync(&sdd->pdev->dev);
struct s3c64xx_spi_csinfo *cs = spi->controller_data;
struct s3c64xx_spi_driver_data *sdd;
struct s3c64xx_spi_info *sci;
- struct spi_message *msg;
- unsigned long flags;
int err;
sdd = spi_master_get_devdata(spi->master);
return -ENODEV;
}
- /* Request gpio only if cs line is asserted by gpio pins */
- if (sdd->cs_gpio) {
- err = gpio_request_one(cs->line, GPIOF_OUT_INIT_HIGH,
- dev_name(&spi->dev));
- if (err) {
- dev_err(&spi->dev,
- "Failed to get /CS gpio [%d]: %d\n",
- cs->line, err);
- goto err_gpio_req;
+ if (!spi_get_ctldata(spi)) {
+ /* Request gpio only if cs line is asserted by gpio pins */
+ if (sdd->cs_gpio) {
+ err = gpio_request_one(cs->line, GPIOF_OUT_INIT_HIGH,
+ dev_name(&spi->dev));
+ if (err) {
+ dev_err(&spi->dev,
+ "Failed to get /CS gpio [%d]: %d\n",
+ cs->line, err);
+ goto err_gpio_req;
+ }
}
- }
- if (!spi_get_ctldata(spi))
spi_set_ctldata(spi, cs);
-
- sci = sdd->cntrlr_info;
-
- spin_lock_irqsave(&sdd->lock, flags);
-
- list_for_each_entry(msg, &sdd->queue, queue) {
- /* Is some mssg is already queued for this device */
- if (msg->spi == spi) {
- dev_err(&spi->dev,
- "setup: attempt while mssg in queue!\n");
- spin_unlock_irqrestore(&sdd->lock, flags);
- err = -EBUSY;
- goto err_msgq;
- }
}
- spin_unlock_irqrestore(&sdd->lock, flags);
+ sci = sdd->cntrlr_info;
pm_runtime_get_sync(&sdd->pdev->dev);
/* setup() returns with device de-selected */
disable_cs(sdd, spi);
-err_msgq:
gpio_free(cs->line);
spi_set_ctldata(spi, NULL);
#else
static struct s3c64xx_spi_info *s3c64xx_spi_parse_dt(struct device *dev)
{
- return dev->platform_data;
+ return dev_get_platdata(dev);
}
#endif
struct resource *mem_res;
struct resource *res;
struct s3c64xx_spi_driver_data *sdd;
- struct s3c64xx_spi_info *sci = pdev->dev.platform_data;
+ struct s3c64xx_spi_info *sci = dev_get_platdata(&pdev->dev);
struct spi_master *master;
int ret, irq;
char clk_name[16];
if (!sdd->pdev->dev.of_node) {
res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (!res) {
- dev_warn(&pdev->dev, "Unable to get SPI tx dma "
- "resource. Switching to poll mode\n");
+ dev_warn(&pdev->dev, "Unable to get SPI tx dma resource. Switching to poll mode\n");
sdd->port_conf->quirks = S3C64XX_SPI_QUIRK_POLL;
} else
sdd->tx_dma.dmach = res->start;
res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
if (!res) {
- dev_warn(&pdev->dev, "Unable to get SPI rx dma "
- "resource. Switching to poll mode\n");
+ dev_warn(&pdev->dev, "Unable to get SPI rx dma resource. Switching to poll mode\n");
sdd->port_conf->quirks = S3C64XX_SPI_QUIRK_POLL;
} else
sdd->rx_dma.dmach = res->start;
SPI_BPW_MASK(8);
/* the spi->mode bits understood by this driver: */
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
+ master->auto_runtime_pm = true;
sdd->regs = devm_ioremap_resource(&pdev->dev, mem_res);
if (IS_ERR(sdd->regs)) {
spin_lock_init(&sdd->lock);
init_completion(&sdd->xfer_completion);
- INIT_LIST_HEAD(&sdd->queue);
ret = devm_request_irq(&pdev->dev, irq, s3c64xx_spi_irq, 0,
"spi-s3c64xx", sdd);
dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d with %d Slaves attached\n",
sdd->port_id, master->num_chipselect);
- dev_dbg(&pdev->dev, "\tIOmem=[0x%x-0x%x]\tDMA=[Rx-%d, Tx-%d]\n",
- mem_res->end, mem_res->start,
+ dev_dbg(&pdev->dev, "\tIOmem=[%pR]\tDMA=[Rx-%d, Tx-%d]\n",
+ mem_res,
sdd->rx_dma.dmach, sdd->tx_dma.dmach);
pm_runtime_enable(&pdev->dev);
/*
* spi master function
*/
-static int hspi_prepare_transfer(struct spi_master *master)
-{
- struct hspi_priv *hspi = spi_master_get_devdata(master);
-
- pm_runtime_get_sync(hspi->dev);
- return 0;
-}
-
-static int hspi_unprepare_transfer(struct spi_master *master)
-{
- struct hspi_priv *hspi = spi_master_get_devdata(master);
-
- pm_runtime_put_sync(hspi->dev);
- return 0;
-}
#define hspi_hw_cs_enable(hspi) hspi_hw_cs_ctrl(hspi, 0)
#define hspi_hw_cs_disable(hspi) hspi_hw_cs_ctrl(hspi, 1)
master->setup = hspi_setup;
master->cleanup = hspi_cleanup;
master->mode_bits = SPI_CPOL | SPI_CPHA;
- master->prepare_transfer_hardware = hspi_prepare_transfer;
+ master->auto_runtime_pm = true;
master->transfer_one_message = hspi_transfer_one_message;
- master->unprepare_transfer_hardware = hspi_unprepare_transfer;
ret = spi_register_master(master);
if (ret < 0) {
dev_err(&pdev->dev, "spi_register_master error.\n");
pm_runtime_enable(&pdev->dev);
- dev_info(&pdev->dev, "probed\n");
-
return 0;
error1:
if (pdev->dev.of_node)
p->info = sh_msiof_spi_parse_dt(&pdev->dev);
else
- p->info = pdev->dev.platform_data;
+ p->info = dev_get_platdata(&pdev->dev);
if (!p->info) {
dev_err(&pdev->dev, "failed to obtain device info\n");
return ret;
}
-static int sh_msiof_spi_runtime_nop(struct device *dev)
-{
- /* Runtime PM callback shared between ->runtime_suspend()
- * and ->runtime_resume(). Simply returns success.
- *
- * This driver re-initializes all registers after
- * pm_runtime_get_sync() anyway so there is no need
- * to save and restore registers here.
- */
- return 0;
-}
-
#ifdef CONFIG_OF
static const struct of_device_id sh_msiof_match[] = {
{ .compatible = "renesas,sh-msiof", },
MODULE_DEVICE_TABLE(of, sh_msiof_match);
#endif
-static struct dev_pm_ops sh_msiof_spi_dev_pm_ops = {
- .runtime_suspend = sh_msiof_spi_runtime_nop,
- .runtime_resume = sh_msiof_spi_runtime_nop,
-};
-
static struct platform_driver sh_msiof_spi_drv = {
.probe = sh_msiof_spi_probe,
.remove = sh_msiof_spi_remove,
.driver = {
.name = "spi_sh_msiof",
.owner = THIS_MODULE,
- .pm = &sh_msiof_spi_dev_pm_ops,
.of_match_table = of_match_ptr(sh_msiof_match),
},
};
sp = spi_master_get_devdata(master);
platform_set_drvdata(dev, sp);
- sp->info = dev->dev.platform_data;
+ sp->info = dev_get_platdata(&dev->dev);
/* setup spi bitbang adaptor */
sp->bitbang.master = spi_master_get(master);
#include <linux/of_gpio.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-direction.h>
+#include <linux/dma-mapping.h>
+#include <linux/sirfsoc_dma.h>
#define DRIVER_NAME "sirfsoc_spi"
#define SIRFSOC_SPI_FIFO_HC(x) (((x) & 0x3F) << 20)
#define SIRFSOC_SPI_FIFO_THD(x) (((x) & 0xFF) << 2)
+/*
+ * only if the rx/tx buffer and transfer size are 4-bytes aligned, we use dma
+ * due to the limitation of dma controller
+ */
+
+#define ALIGNED(x) (!((u32)x & 0x3))
+#define IS_DMA_VALID(x) (x && ALIGNED(x->tx_buf) && ALIGNED(x->rx_buf) && \
+ ALIGNED(x->len) && (x->len < 2 * PAGE_SIZE))
+
struct sirfsoc_spi {
struct spi_bitbang bitbang;
- struct completion done;
+ struct completion rx_done;
+ struct completion tx_done;
void __iomem *base;
u32 ctrl_freq; /* SPI controller clock speed */
void (*tx_word) (struct sirfsoc_spi *);
/* number of words left to be tranmitted/received */
- unsigned int left_tx_cnt;
- unsigned int left_rx_cnt;
+ unsigned int left_tx_word;
+ unsigned int left_rx_word;
+
+ /* rx & tx DMA channels */
+ struct dma_chan *rx_chan;
+ struct dma_chan *tx_chan;
+ dma_addr_t src_start;
+ dma_addr_t dst_start;
+ void *dummypage;
+ int word_width; /* in bytes */
int chipselect[0];
};
sspi->rx = rx;
}
- sspi->left_rx_cnt--;
+ sspi->left_rx_word--;
}
static void spi_sirfsoc_tx_word_u8(struct sirfsoc_spi *sspi)
}
writel(data, sspi->base + SIRFSOC_SPI_TXFIFO_DATA);
- sspi->left_tx_cnt--;
+ sspi->left_tx_word--;
}
static void spi_sirfsoc_rx_word_u16(struct sirfsoc_spi *sspi)
sspi->rx = rx;
}
- sspi->left_rx_cnt--;
+ sspi->left_rx_word--;
}
static void spi_sirfsoc_tx_word_u16(struct sirfsoc_spi *sspi)
}
writel(data, sspi->base + SIRFSOC_SPI_TXFIFO_DATA);
- sspi->left_tx_cnt--;
+ sspi->left_tx_word--;
}
static void spi_sirfsoc_rx_word_u32(struct sirfsoc_spi *sspi)
sspi->rx = rx;
}
- sspi->left_rx_cnt--;
+ sspi->left_rx_word--;
}
}
writel(data, sspi->base + SIRFSOC_SPI_TXFIFO_DATA);
- sspi->left_tx_cnt--;
+ sspi->left_tx_word--;
}
static irqreturn_t spi_sirfsoc_irq(int irq, void *dev_id)
/* Error Conditions */
if (spi_stat & SIRFSOC_SPI_RX_OFLOW ||
spi_stat & SIRFSOC_SPI_TX_UFLOW) {
- complete(&sspi->done);
+ complete(&sspi->rx_done);
writel(0x0, sspi->base + SIRFSOC_SPI_INT_EN);
}
| SIRFSOC_SPI_RXFIFO_THD_REACH))
while (!((readl(sspi->base + SIRFSOC_SPI_RXFIFO_STATUS)
& SIRFSOC_SPI_FIFO_EMPTY)) &&
- sspi->left_rx_cnt)
+ sspi->left_rx_word)
sspi->rx_word(sspi);
if (spi_stat & (SIRFSOC_SPI_FIFO_EMPTY
| SIRFSOC_SPI_TXFIFO_THD_REACH))
while (!((readl(sspi->base + SIRFSOC_SPI_TXFIFO_STATUS)
& SIRFSOC_SPI_FIFO_FULL)) &&
- sspi->left_tx_cnt)
+ sspi->left_tx_word)
sspi->tx_word(sspi);
/* Received all words */
- if ((sspi->left_rx_cnt == 0) && (sspi->left_tx_cnt == 0)) {
- complete(&sspi->done);
+ if ((sspi->left_rx_word == 0) && (sspi->left_tx_word == 0)) {
+ complete(&sspi->rx_done);
writel(0x0, sspi->base + SIRFSOC_SPI_INT_EN);
}
return IRQ_HANDLED;
}
+static void spi_sirfsoc_dma_fini_callback(void *data)
+{
+ struct completion *dma_complete = data;
+
+ complete(dma_complete);
+}
+
static int spi_sirfsoc_transfer(struct spi_device *spi, struct spi_transfer *t)
{
struct sirfsoc_spi *sspi;
int timeout = t->len * 10;
sspi = spi_master_get_devdata(spi->master);
- sspi->tx = t->tx_buf;
- sspi->rx = t->rx_buf;
- sspi->left_tx_cnt = sspi->left_rx_cnt = t->len;
- INIT_COMPLETION(sspi->done);
+ sspi->tx = t->tx_buf ? t->tx_buf : sspi->dummypage;
+ sspi->rx = t->rx_buf ? t->rx_buf : sspi->dummypage;
+ sspi->left_tx_word = sspi->left_rx_word = t->len / sspi->word_width;
+ INIT_COMPLETION(sspi->rx_done);
+ INIT_COMPLETION(sspi->tx_done);
writel(SIRFSOC_SPI_INT_MASK_ALL, sspi->base + SIRFSOC_SPI_INT_STATUS);
- if (t->len == 1) {
+ if (sspi->left_tx_word == 1) {
writel(readl(sspi->base + SIRFSOC_SPI_CTRL) |
SIRFSOC_SPI_ENA_AUTO_CLR,
sspi->base + SIRFSOC_SPI_CTRL);
writel(0, sspi->base + SIRFSOC_SPI_TX_DMA_IO_LEN);
writel(0, sspi->base + SIRFSOC_SPI_RX_DMA_IO_LEN);
- } else if ((t->len > 1) && (t->len < SIRFSOC_SPI_DAT_FRM_LEN_MAX)) {
+ } else if ((sspi->left_tx_word > 1) && (sspi->left_tx_word <
+ SIRFSOC_SPI_DAT_FRM_LEN_MAX)) {
writel(readl(sspi->base + SIRFSOC_SPI_CTRL) |
SIRFSOC_SPI_MUL_DAT_MODE |
SIRFSOC_SPI_ENA_AUTO_CLR,
sspi->base + SIRFSOC_SPI_CTRL);
- writel(t->len - 1, sspi->base + SIRFSOC_SPI_TX_DMA_IO_LEN);
- writel(t->len - 1, sspi->base + SIRFSOC_SPI_RX_DMA_IO_LEN);
+ writel(sspi->left_tx_word - 1,
+ sspi->base + SIRFSOC_SPI_TX_DMA_IO_LEN);
+ writel(sspi->left_tx_word - 1,
+ sspi->base + SIRFSOC_SPI_RX_DMA_IO_LEN);
} else {
writel(readl(sspi->base + SIRFSOC_SPI_CTRL),
sspi->base + SIRFSOC_SPI_CTRL);
writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
writel(SIRFSOC_SPI_FIFO_START, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
- /* Send the first word to trigger the whole tx/rx process */
- sspi->tx_word(sspi);
+ if (IS_DMA_VALID(t)) {
+ struct dma_async_tx_descriptor *rx_desc, *tx_desc;
+
+ sspi->dst_start = dma_map_single(&spi->dev, sspi->rx, t->len, DMA_FROM_DEVICE);
+ rx_desc = dmaengine_prep_slave_single(sspi->rx_chan,
+ sspi->dst_start, t->len, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ rx_desc->callback = spi_sirfsoc_dma_fini_callback;
+ rx_desc->callback_param = &sspi->rx_done;
+
+ sspi->src_start = dma_map_single(&spi->dev, (void *)sspi->tx, t->len, DMA_TO_DEVICE);
+ tx_desc = dmaengine_prep_slave_single(sspi->tx_chan,
+ sspi->src_start, t->len, DMA_MEM_TO_DEV,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ tx_desc->callback = spi_sirfsoc_dma_fini_callback;
+ tx_desc->callback_param = &sspi->tx_done;
+
+ dmaengine_submit(tx_desc);
+ dmaengine_submit(rx_desc);
+ dma_async_issue_pending(sspi->tx_chan);
+ dma_async_issue_pending(sspi->rx_chan);
+ } else {
+ /* Send the first word to trigger the whole tx/rx process */
+ sspi->tx_word(sspi);
+
+ writel(SIRFSOC_SPI_RX_OFLOW_INT_EN | SIRFSOC_SPI_TX_UFLOW_INT_EN |
+ SIRFSOC_SPI_RXFIFO_THD_INT_EN | SIRFSOC_SPI_TXFIFO_THD_INT_EN |
+ SIRFSOC_SPI_FRM_END_INT_EN | SIRFSOC_SPI_RXFIFO_FULL_INT_EN |
+ SIRFSOC_SPI_TXFIFO_EMPTY_INT_EN, sspi->base + SIRFSOC_SPI_INT_EN);
+ }
- writel(SIRFSOC_SPI_RX_OFLOW_INT_EN | SIRFSOC_SPI_TX_UFLOW_INT_EN |
- SIRFSOC_SPI_RXFIFO_THD_INT_EN | SIRFSOC_SPI_TXFIFO_THD_INT_EN |
- SIRFSOC_SPI_FRM_END_INT_EN | SIRFSOC_SPI_RXFIFO_FULL_INT_EN |
- SIRFSOC_SPI_TXFIFO_EMPTY_INT_EN, sspi->base + SIRFSOC_SPI_INT_EN);
writel(SIRFSOC_SPI_RX_EN | SIRFSOC_SPI_TX_EN, sspi->base + SIRFSOC_SPI_TX_RX_EN);
- if (wait_for_completion_timeout(&sspi->done, timeout) == 0)
+ if (!IS_DMA_VALID(t)) { /* for PIO */
+ if (wait_for_completion_timeout(&sspi->rx_done, timeout) == 0)
+ dev_err(&spi->dev, "transfer timeout\n");
+ } else if (wait_for_completion_timeout(&sspi->rx_done, timeout) == 0) {
dev_err(&spi->dev, "transfer timeout\n");
+ dmaengine_terminate_all(sspi->rx_chan);
+ } else
+ sspi->left_rx_word = 0;
+
+ /*
+ * we only wait tx-done event if transferring by DMA. for PIO,
+ * we get rx data by writing tx data, so if rx is done, tx has
+ * done earlier
+ */
+ if (IS_DMA_VALID(t)) {
+ if (wait_for_completion_timeout(&sspi->tx_done, timeout) == 0) {
+ dev_err(&spi->dev, "transfer timeout\n");
+ dmaengine_terminate_all(sspi->tx_chan);
+ }
+ }
+
+ if (IS_DMA_VALID(t)) {
+ dma_unmap_single(&spi->dev, sspi->src_start, t->len, DMA_TO_DEVICE);
+ dma_unmap_single(&spi->dev, sspi->dst_start, t->len, DMA_FROM_DEVICE);
+ }
/* TX, RX FIFO stop */
writel(0, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
writel(0, sspi->base + SIRFSOC_SPI_TX_RX_EN);
writel(0, sspi->base + SIRFSOC_SPI_INT_EN);
- return t->len - sspi->left_rx_cnt;
+ return t->len - sspi->left_rx_word * sspi->word_width;
}
static void spi_sirfsoc_chipselect(struct spi_device *spi, int value)
if (sspi->chipselect[spi->chip_select] == 0) {
u32 regval = readl(sspi->base + SIRFSOC_SPI_CTRL);
- regval |= SIRFSOC_SPI_CS_IO_OUT;
switch (value) {
case BITBANG_CS_ACTIVE:
if (spi->mode & SPI_CS_HIGH)
bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word;
hz = t && t->speed_hz ? t->speed_hz : spi->max_speed_hz;
- /* Enable IO mode for RX, TX */
- writel(SIRFSOC_SPI_IO_MODE_SEL, sspi->base + SIRFSOC_SPI_TX_DMA_IO_CTRL);
- writel(SIRFSOC_SPI_IO_MODE_SEL, sspi->base + SIRFSOC_SPI_RX_DMA_IO_CTRL);
regval = (sspi->ctrl_freq / (2 * hz)) - 1;
-
if (regval > 0xFFFF || regval < 0) {
dev_err(&spi->dev, "Speed %d not supported\n", hz);
return -EINVAL;
SIRFSOC_SPI_FIFO_WIDTH_BYTE;
rxfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
SIRFSOC_SPI_FIFO_WIDTH_BYTE;
+ sspi->word_width = 1;
break;
case 12:
case 16:
SIRFSOC_SPI_FIFO_WIDTH_WORD;
rxfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
SIRFSOC_SPI_FIFO_WIDTH_WORD;
+ sspi->word_width = 2;
break;
case 32:
regval |= SIRFSOC_SPI_TRAN_DAT_FORMAT_32;
SIRFSOC_SPI_FIFO_WIDTH_DWORD;
rxfifo_ctrl = SIRFSOC_SPI_FIFO_THD(SIRFSOC_SPI_FIFO_SIZE / 2) |
SIRFSOC_SPI_FIFO_WIDTH_DWORD;
+ sspi->word_width = 4;
break;
default:
BUG();
writel(rxfifo_ctrl, sspi->base + SIRFSOC_SPI_RXFIFO_CTRL);
writel(regval, sspi->base + SIRFSOC_SPI_CTRL);
+
+ if (IS_DMA_VALID(t)) {
+ /* Enable DMA mode for RX, TX */
+ writel(0, sspi->base + SIRFSOC_SPI_TX_DMA_IO_CTRL);
+ writel(SIRFSOC_SPI_RX_DMA_FLUSH, sspi->base + SIRFSOC_SPI_RX_DMA_IO_CTRL);
+ } else {
+ /* Enable IO mode for RX, TX */
+ writel(SIRFSOC_SPI_IO_MODE_SEL, sspi->base + SIRFSOC_SPI_TX_DMA_IO_CTRL);
+ writel(SIRFSOC_SPI_IO_MODE_SEL, sspi->base + SIRFSOC_SPI_RX_DMA_IO_CTRL);
+ }
+
return 0;
}
struct spi_master *master;
struct resource *mem_res;
int num_cs, cs_gpio, irq;
+ u32 rx_dma_ch, tx_dma_ch;
+ dma_cap_mask_t dma_cap_mask;
int i;
int ret;
goto err_cs;
}
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "sirf,spi-dma-rx-channel", &rx_dma_ch);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Unable to get rx dma channel\n");
+ goto err_cs;
+ }
+
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "sirf,spi-dma-tx-channel", &tx_dma_ch);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Unable to get tx dma channel\n");
+ goto err_cs;
+ }
+
master = spi_alloc_master(&pdev->dev, sizeof(*sspi) + sizeof(int) * num_cs);
if (!master) {
dev_err(&pdev->dev, "Unable to allocate SPI master\n");
platform_set_drvdata(pdev, master);
sspi = spi_master_get_devdata(master);
- mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem_res) {
- dev_err(&pdev->dev, "Unable to get IO resource\n");
- ret = -ENODEV;
- goto free_master;
- }
master->num_chipselect = num_cs;
for (i = 0; i < master->num_chipselect; i++) {
}
}
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
sspi->base = devm_ioremap_resource(&pdev->dev, mem_res);
if (IS_ERR(sspi->base)) {
ret = PTR_ERR(sspi->base);
sspi->bitbang.txrx_bufs = spi_sirfsoc_transfer;
sspi->bitbang.master->setup = spi_sirfsoc_setup;
master->bus_num = pdev->id;
+ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | SPI_CS_HIGH;
master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(12) |
SPI_BPW_MASK(16) | SPI_BPW_MASK(32);
sspi->bitbang.master->dev.of_node = pdev->dev.of_node;
+ /* request DMA channels */
+ dma_cap_zero(dma_cap_mask);
+ dma_cap_set(DMA_INTERLEAVE, dma_cap_mask);
+
+ sspi->rx_chan = dma_request_channel(dma_cap_mask, (dma_filter_fn)sirfsoc_dma_filter_id,
+ (void *)rx_dma_ch);
+ if (!sspi->rx_chan) {
+ dev_err(&pdev->dev, "can not allocate rx dma channel\n");
+ ret = -ENODEV;
+ goto free_master;
+ }
+ sspi->tx_chan = dma_request_channel(dma_cap_mask, (dma_filter_fn)sirfsoc_dma_filter_id,
+ (void *)tx_dma_ch);
+ if (!sspi->tx_chan) {
+ dev_err(&pdev->dev, "can not allocate tx dma channel\n");
+ ret = -ENODEV;
+ goto free_rx_dma;
+ }
+
sspi->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(sspi->clk)) {
- ret = -EINVAL;
- goto free_master;
+ ret = PTR_ERR(sspi->clk);
+ goto free_tx_dma;
}
clk_prepare_enable(sspi->clk);
sspi->ctrl_freq = clk_get_rate(sspi->clk);
- init_completion(&sspi->done);
+ init_completion(&sspi->rx_done);
+ init_completion(&sspi->tx_done);
writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_RXFIFO_OP);
writel(SIRFSOC_SPI_FIFO_RESET, sspi->base + SIRFSOC_SPI_TXFIFO_OP);
/* We are not using dummy delay between command and data */
writel(0, sspi->base + SIRFSOC_SPI_DUMMY_DELAY_CTL);
+ sspi->dummypage = kmalloc(2 * PAGE_SIZE, GFP_KERNEL);
+ if (!sspi->dummypage) {
+ ret = -ENOMEM;
+ goto free_clk;
+ }
+
ret = spi_bitbang_start(&sspi->bitbang);
if (ret)
- goto free_clk;
+ goto free_dummypage;
dev_info(&pdev->dev, "registerred, bus number = %d\n", master->bus_num);
return 0;
-
+free_dummypage:
+ kfree(sspi->dummypage);
free_clk:
clk_disable_unprepare(sspi->clk);
clk_put(sspi->clk);
+free_tx_dma:
+ dma_release_channel(sspi->tx_chan);
+free_rx_dma:
+ dma_release_channel(sspi->rx_chan);
free_master:
spi_master_put(master);
err_cs:
if (sspi->chipselect[i] > 0)
gpio_free(sspi->chipselect[i]);
}
+ kfree(sspi->dummypage);
clk_disable_unprepare(sspi->clk);
clk_put(sspi->clk);
+ dma_release_channel(sspi->rx_chan);
+ dma_release_channel(sspi->tx_chan);
spi_master_put(master);
return 0;
}
#ifdef CONFIG_PM
static int spi_sirfsoc_suspend(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct spi_master *master = platform_get_drvdata(pdev);
+ struct spi_master *master = dev_get_drvdata(dev);
struct sirfsoc_spi *sspi = spi_master_get_devdata(master);
clk_disable(sspi->clk);
static int spi_sirfsoc_resume(struct device *dev)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct spi_master *master = platform_get_drvdata(pdev);
+ struct spi_master *master = dev_get_drvdata(dev);
struct sirfsoc_spi *sspi = spi_master_get_devdata(master);
clk_enable(sspi->clk);
msg->status = 0;
msg->actual_length = 0;
- ret = pm_runtime_get_sync(tspi->dev);
- if (ret < 0) {
- dev_err(tspi->dev, "runtime PM get failed: %d\n", ret);
- msg->status = ret;
- spi_finalize_current_message(master);
- return ret;
- }
-
single_xfer = list_is_singular(&msg->transfers);
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
INIT_COMPLETION(tspi->xfer_completion);
ret = 0;
exit:
tegra_spi_writel(tspi, tspi->def_command1_reg, SPI_COMMAND1);
- pm_runtime_put(tspi->dev);
msg->status = ret;
spi_finalize_current_message(master);
return ret;
master->transfer_one_message = tegra_spi_transfer_one_message;
master->num_chipselect = MAX_CHIP_SELECT;
master->bus_num = -1;
+ master->auto_runtime_pm = true;
tspi->master = master;
tspi->dev = &pdev->dev;
spin_lock_init(&tspi->lock);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!r) {
- dev_err(&pdev->dev, "No IO memory resource\n");
- ret = -ENODEV;
- goto exit_free_master;
- }
- tspi->phys = r->start;
tspi->base = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(tspi->base)) {
ret = PTR_ERR(tspi->base);
- dev_err(&pdev->dev, "ioremap failed: err = %d\n", ret);
goto exit_free_master;
}
+ tspi->phys = r->start;
spi_irq = platform_get_irq(pdev, 0);
tspi->irq = spi_irq;
struct spi_device *spi = msg->spi;
int ret;
- ret = pm_runtime_get_sync(tsd->dev);
- if (ret < 0) {
- dev_err(tsd->dev, "pm_runtime_get() failed, err = %d\n", ret);
- return ret;
- }
-
msg->status = 0;
msg->actual_length = 0;
single_xfer = list_is_singular(&msg->transfers);
tegra_sflash_writel(tsd, tsd->def_command_reg, SPI_COMMAND);
msg->status = ret;
spi_finalize_current_message(master);
- pm_runtime_put(tsd->dev);
return ret;
}
master->mode_bits = SPI_CPOL | SPI_CPHA;
master->setup = tegra_sflash_setup;
master->transfer_one_message = tegra_sflash_transfer_one_message;
+ master->auto_runtime_pm = true;
master->num_chipselect = MAX_CHIP_SELECT;
master->bus_num = -1;
msg->status = 0;
msg->actual_length = 0;
- ret = pm_runtime_get_sync(tspi->dev);
- if (ret < 0) {
- dev_err(tspi->dev, "runtime get failed: %d\n", ret);
- goto done;
- }
single_xfer = list_is_singular(&msg->transfers);
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
exit:
tegra_slink_writel(tspi, tspi->def_command_reg, SLINK_COMMAND);
tegra_slink_writel(tspi, tspi->def_command2_reg, SLINK_COMMAND2);
- pm_runtime_put(tspi->dev);
-done:
msg->status = ret;
spi_finalize_current_message(master);
return ret;
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
master->setup = tegra_slink_setup;
master->transfer_one_message = tegra_slink_transfer_one_message;
+ master->auto_runtime_pm = true;
master->num_chipselect = MAX_CHIP_SELECT;
master->bus_num = -1;
--- /dev/null
+/*
+ * TI QSPI driver
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ * Author: Sourav Poddar <sourav.poddar@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GPLv2.
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/omap-dma.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
+
+#include <linux/spi/spi.h>
+
+struct ti_qspi_regs {
+ u32 clkctrl;
+};
+
+struct ti_qspi {
+ struct completion transfer_complete;
+
+ /* IRQ synchronization */
+ spinlock_t lock;
+
+ /* list synchronization */
+ struct mutex list_lock;
+
+ struct spi_master *master;
+ void __iomem *base;
+ struct clk *fclk;
+ struct device *dev;
+
+ struct ti_qspi_regs ctx_reg;
+
+ u32 spi_max_frequency;
+ u32 cmd;
+ u32 dc;
+ u32 stat;
+};
+
+#define QSPI_PID (0x0)
+#define QSPI_SYSCONFIG (0x10)
+#define QSPI_INTR_STATUS_RAW_SET (0x20)
+#define QSPI_INTR_STATUS_ENABLED_CLEAR (0x24)
+#define QSPI_INTR_ENABLE_SET_REG (0x28)
+#define QSPI_INTR_ENABLE_CLEAR_REG (0x2c)
+#define QSPI_SPI_CLOCK_CNTRL_REG (0x40)
+#define QSPI_SPI_DC_REG (0x44)
+#define QSPI_SPI_CMD_REG (0x48)
+#define QSPI_SPI_STATUS_REG (0x4c)
+#define QSPI_SPI_DATA_REG (0x50)
+#define QSPI_SPI_SETUP0_REG (0x54)
+#define QSPI_SPI_SWITCH_REG (0x64)
+#define QSPI_SPI_SETUP1_REG (0x58)
+#define QSPI_SPI_SETUP2_REG (0x5c)
+#define QSPI_SPI_SETUP3_REG (0x60)
+#define QSPI_SPI_DATA_REG_1 (0x68)
+#define QSPI_SPI_DATA_REG_2 (0x6c)
+#define QSPI_SPI_DATA_REG_3 (0x70)
+
+#define QSPI_COMPLETION_TIMEOUT msecs_to_jiffies(2000)
+
+#define QSPI_FCLK 192000000
+
+/* Clock Control */
+#define QSPI_CLK_EN (1 << 31)
+#define QSPI_CLK_DIV_MAX 0xffff
+
+/* Command */
+#define QSPI_EN_CS(n) (n << 28)
+#define QSPI_WLEN(n) ((n - 1) << 19)
+#define QSPI_3_PIN (1 << 18)
+#define QSPI_RD_SNGL (1 << 16)
+#define QSPI_WR_SNGL (2 << 16)
+#define QSPI_RD_DUAL (3 << 16)
+#define QSPI_RD_QUAD (7 << 16)
+#define QSPI_INVAL (4 << 16)
+#define QSPI_WC_CMD_INT_EN (1 << 14)
+#define QSPI_FLEN(n) ((n - 1) << 0)
+
+/* STATUS REGISTER */
+#define WC 0x02
+
+/* INTERRUPT REGISTER */
+#define QSPI_WC_INT_EN (1 << 1)
+#define QSPI_WC_INT_DISABLE (1 << 1)
+
+/* Device Control */
+#define QSPI_DD(m, n) (m << (3 + n * 8))
+#define QSPI_CKPHA(n) (1 << (2 + n * 8))
+#define QSPI_CSPOL(n) (1 << (1 + n * 8))
+#define QSPI_CKPOL(n) (1 << (n * 8))
+
+#define QSPI_FRAME 4096
+
+#define QSPI_AUTOSUSPEND_TIMEOUT 2000
+
+static inline unsigned long ti_qspi_read(struct ti_qspi *qspi,
+ unsigned long reg)
+{
+ return readl(qspi->base + reg);
+}
+
+static inline void ti_qspi_write(struct ti_qspi *qspi,
+ unsigned long val, unsigned long reg)
+{
+ writel(val, qspi->base + reg);
+}
+
+static int ti_qspi_setup(struct spi_device *spi)
+{
+ struct ti_qspi *qspi = spi_master_get_devdata(spi->master);
+ struct ti_qspi_regs *ctx_reg = &qspi->ctx_reg;
+ int clk_div = 0, ret;
+ u32 clk_ctrl_reg, clk_rate, clk_mask;
+
+ if (spi->master->busy) {
+ dev_dbg(qspi->dev, "master busy doing other trasnfers\n");
+ return -EBUSY;
+ }
+
+ if (!qspi->spi_max_frequency) {
+ dev_err(qspi->dev, "spi max frequency not defined\n");
+ return -EINVAL;
+ }
+
+ clk_rate = clk_get_rate(qspi->fclk);
+
+ clk_div = DIV_ROUND_UP(clk_rate, qspi->spi_max_frequency) - 1;
+
+ if (clk_div < 0) {
+ dev_dbg(qspi->dev, "clock divider < 0, using /1 divider\n");
+ return -EINVAL;
+ }
+
+ if (clk_div > QSPI_CLK_DIV_MAX) {
+ dev_dbg(qspi->dev, "clock divider >%d , using /%d divider\n",
+ QSPI_CLK_DIV_MAX, QSPI_CLK_DIV_MAX + 1);
+ return -EINVAL;
+ }
+
+ dev_dbg(qspi->dev, "hz: %d, clock divider %d\n",
+ qspi->spi_max_frequency, clk_div);
+
+ ret = pm_runtime_get_sync(qspi->dev);
+ if (ret) {
+ dev_err(qspi->dev, "pm_runtime_get_sync() failed\n");
+ return ret;
+ }
+
+ clk_ctrl_reg = ti_qspi_read(qspi, QSPI_SPI_CLOCK_CNTRL_REG);
+
+ clk_ctrl_reg &= ~QSPI_CLK_EN;
+
+ /* disable SCLK */
+ ti_qspi_write(qspi, clk_ctrl_reg, QSPI_SPI_CLOCK_CNTRL_REG);
+
+ /* enable SCLK */
+ clk_mask = QSPI_CLK_EN | clk_div;
+ ti_qspi_write(qspi, clk_mask, QSPI_SPI_CLOCK_CNTRL_REG);
+ ctx_reg->clkctrl = clk_mask;
+
+ pm_runtime_mark_last_busy(qspi->dev);
+ ret = pm_runtime_put_autosuspend(qspi->dev);
+ if (ret < 0) {
+ dev_err(qspi->dev, "pm_runtime_put_autosuspend() failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ti_qspi_restore_ctx(struct ti_qspi *qspi)
+{
+ struct ti_qspi_regs *ctx_reg = &qspi->ctx_reg;
+
+ ti_qspi_write(qspi, ctx_reg->clkctrl, QSPI_SPI_CLOCK_CNTRL_REG);
+}
+
+static int qspi_write_msg(struct ti_qspi *qspi, struct spi_transfer *t)
+{
+ int wlen, count, ret;
+ unsigned int cmd;
+ const u8 *txbuf;
+
+ txbuf = t->tx_buf;
+ cmd = qspi->cmd | QSPI_WR_SNGL;
+ count = t->len;
+ wlen = t->bits_per_word;
+
+ while (count) {
+ switch (wlen) {
+ case 8:
+ dev_dbg(qspi->dev, "tx cmd %08x dc %08x data %02x\n",
+ cmd, qspi->dc, *txbuf);
+ writeb(*txbuf, qspi->base + QSPI_SPI_DATA_REG);
+ ti_qspi_write(qspi, cmd, QSPI_SPI_CMD_REG);
+ ret = wait_for_completion_timeout(&qspi->transfer_complete,
+ QSPI_COMPLETION_TIMEOUT);
+ if (ret == 0) {
+ dev_err(qspi->dev, "write timed out\n");
+ return -ETIMEDOUT;
+ }
+ txbuf += 1;
+ count -= 1;
+ break;
+ case 16:
+ dev_dbg(qspi->dev, "tx cmd %08x dc %08x data %04x\n",
+ cmd, qspi->dc, *txbuf);
+ writew(*((u16 *)txbuf), qspi->base + QSPI_SPI_DATA_REG);
+ ti_qspi_write(qspi, cmd, QSPI_SPI_CMD_REG);
+ ret = wait_for_completion_timeout(&qspi->transfer_complete,
+ QSPI_COMPLETION_TIMEOUT);
+ if (ret == 0) {
+ dev_err(qspi->dev, "write timed out\n");
+ return -ETIMEDOUT;
+ }
+ txbuf += 2;
+ count -= 2;
+ break;
+ case 32:
+ dev_dbg(qspi->dev, "tx cmd %08x dc %08x data %08x\n",
+ cmd, qspi->dc, *txbuf);
+ writel(*((u32 *)txbuf), qspi->base + QSPI_SPI_DATA_REG);
+ ti_qspi_write(qspi, cmd, QSPI_SPI_CMD_REG);
+ ret = wait_for_completion_timeout(&qspi->transfer_complete,
+ QSPI_COMPLETION_TIMEOUT);
+ if (ret == 0) {
+ dev_err(qspi->dev, "write timed out\n");
+ return -ETIMEDOUT;
+ }
+ txbuf += 4;
+ count -= 4;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int qspi_read_msg(struct ti_qspi *qspi, struct spi_transfer *t)
+{
+ int wlen, count, ret;
+ unsigned int cmd;
+ u8 *rxbuf;
+
+ rxbuf = t->rx_buf;
+ cmd = qspi->cmd;
+ switch (t->rx_nbits) {
+ case SPI_NBITS_DUAL:
+ cmd |= QSPI_RD_DUAL;
+ break;
+ case SPI_NBITS_QUAD:
+ cmd |= QSPI_RD_QUAD;
+ break;
+ default:
+ cmd |= QSPI_RD_SNGL;
+ break;
+ }
+ count = t->len;
+ wlen = t->bits_per_word;
+
+ while (count) {
+ dev_dbg(qspi->dev, "rx cmd %08x dc %08x\n", cmd, qspi->dc);
+ ti_qspi_write(qspi, cmd, QSPI_SPI_CMD_REG);
+ ret = wait_for_completion_timeout(&qspi->transfer_complete,
+ QSPI_COMPLETION_TIMEOUT);
+ if (ret == 0) {
+ dev_err(qspi->dev, "read timed out\n");
+ return -ETIMEDOUT;
+ }
+ switch (wlen) {
+ case 8:
+ *rxbuf = readb(qspi->base + QSPI_SPI_DATA_REG);
+ rxbuf += 1;
+ count -= 1;
+ break;
+ case 16:
+ *((u16 *)rxbuf) = readw(qspi->base + QSPI_SPI_DATA_REG);
+ rxbuf += 2;
+ count -= 2;
+ break;
+ case 32:
+ *((u32 *)rxbuf) = readl(qspi->base + QSPI_SPI_DATA_REG);
+ rxbuf += 4;
+ count -= 4;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int qspi_transfer_msg(struct ti_qspi *qspi, struct spi_transfer *t)
+{
+ int ret;
+
+ if (t->tx_buf) {
+ ret = qspi_write_msg(qspi, t);
+ if (ret) {
+ dev_dbg(qspi->dev, "Error while writing\n");
+ return ret;
+ }
+ }
+
+ if (t->rx_buf) {
+ ret = qspi_read_msg(qspi, t);
+ if (ret) {
+ dev_dbg(qspi->dev, "Error while reading\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int ti_qspi_start_transfer_one(struct spi_master *master,
+ struct spi_message *m)
+{
+ struct ti_qspi *qspi = spi_master_get_devdata(master);
+ struct spi_device *spi = m->spi;
+ struct spi_transfer *t;
+ int status = 0, ret;
+ int frame_length;
+
+ /* setup device control reg */
+ qspi->dc = 0;
+
+ if (spi->mode & SPI_CPHA)
+ qspi->dc |= QSPI_CKPHA(spi->chip_select);
+ if (spi->mode & SPI_CPOL)
+ qspi->dc |= QSPI_CKPOL(spi->chip_select);
+ if (spi->mode & SPI_CS_HIGH)
+ qspi->dc |= QSPI_CSPOL(spi->chip_select);
+
+ frame_length = (m->frame_length << 3) / spi->bits_per_word;
+
+ frame_length = clamp(frame_length, 0, QSPI_FRAME);
+
+ /* setup command reg */
+ qspi->cmd = 0;
+ qspi->cmd |= QSPI_EN_CS(spi->chip_select);
+ qspi->cmd |= QSPI_FLEN(frame_length);
+ qspi->cmd |= QSPI_WC_CMD_INT_EN;
+
+ ti_qspi_write(qspi, QSPI_WC_INT_EN, QSPI_INTR_ENABLE_SET_REG);
+ ti_qspi_write(qspi, qspi->dc, QSPI_SPI_DC_REG);
+
+ mutex_lock(&qspi->list_lock);
+
+ list_for_each_entry(t, &m->transfers, transfer_list) {
+ qspi->cmd |= QSPI_WLEN(t->bits_per_word);
+
+ ret = qspi_transfer_msg(qspi, t);
+ if (ret) {
+ dev_dbg(qspi->dev, "transfer message failed\n");
+ mutex_unlock(&qspi->list_lock);
+ return -EINVAL;
+ }
+
+ m->actual_length += t->len;
+ }
+
+ mutex_unlock(&qspi->list_lock);
+
+ m->status = status;
+ spi_finalize_current_message(master);
+
+ ti_qspi_write(qspi, qspi->cmd | QSPI_INVAL, QSPI_SPI_CMD_REG);
+
+ return status;
+}
+
+static irqreturn_t ti_qspi_isr(int irq, void *dev_id)
+{
+ struct ti_qspi *qspi = dev_id;
+ u16 int_stat;
+
+ irqreturn_t ret = IRQ_HANDLED;
+
+ spin_lock(&qspi->lock);
+
+ int_stat = ti_qspi_read(qspi, QSPI_INTR_STATUS_ENABLED_CLEAR);
+ qspi->stat = ti_qspi_read(qspi, QSPI_SPI_STATUS_REG);
+
+ if (!int_stat) {
+ dev_dbg(qspi->dev, "No IRQ triggered\n");
+ ret = IRQ_NONE;
+ goto out;
+ }
+
+ ret = IRQ_WAKE_THREAD;
+
+ ti_qspi_write(qspi, QSPI_WC_INT_DISABLE, QSPI_INTR_ENABLE_CLEAR_REG);
+ ti_qspi_write(qspi, QSPI_WC_INT_DISABLE,
+ QSPI_INTR_STATUS_ENABLED_CLEAR);
+
+out:
+ spin_unlock(&qspi->lock);
+
+ return ret;
+}
+
+static irqreturn_t ti_qspi_threaded_isr(int this_irq, void *dev_id)
+{
+ struct ti_qspi *qspi = dev_id;
+ unsigned long flags;
+
+ spin_lock_irqsave(&qspi->lock, flags);
+
+ if (qspi->stat & WC)
+ complete(&qspi->transfer_complete);
+
+ spin_unlock_irqrestore(&qspi->lock, flags);
+
+ ti_qspi_write(qspi, QSPI_WC_INT_EN, QSPI_INTR_ENABLE_SET_REG);
+
+ return IRQ_HANDLED;
+}
+
+static int ti_qspi_runtime_resume(struct device *dev)
+{
+ struct ti_qspi *qspi;
+ struct spi_master *master;
+
+ master = dev_get_drvdata(dev);
+ qspi = spi_master_get_devdata(master);
+ ti_qspi_restore_ctx(qspi);
+
+ return 0;
+}
+
+static const struct of_device_id ti_qspi_match[] = {
+ {.compatible = "ti,dra7xxx-qspi" },
+ {.compatible = "ti,am4372-qspi" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ti_qspi_match);
+
+static int ti_qspi_probe(struct platform_device *pdev)
+{
+ struct ti_qspi *qspi;
+ struct spi_master *master;
+ struct resource *r;
+ struct device_node *np = pdev->dev.of_node;
+ u32 max_freq;
+ int ret = 0, num_cs, irq;
+
+ master = spi_alloc_master(&pdev->dev, sizeof(*qspi));
+ if (!master)
+ return -ENOMEM;
+
+ master->mode_bits = SPI_CPOL | SPI_CPHA;
+
+ master->bus_num = -1;
+ master->flags = SPI_MASTER_HALF_DUPLEX;
+ master->setup = ti_qspi_setup;
+ master->auto_runtime_pm = true;
+ master->transfer_one_message = ti_qspi_start_transfer_one;
+ master->dev.of_node = pdev->dev.of_node;
+ master->bits_per_word_mask = BIT(32 - 1) | BIT(16 - 1) | BIT(8 - 1);
+
+ if (!of_property_read_u32(np, "num-cs", &num_cs))
+ master->num_chipselect = num_cs;
+
+ platform_set_drvdata(pdev, master);
+
+ qspi = spi_master_get_devdata(master);
+ qspi->master = master;
+ qspi->dev = &pdev->dev;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "no irq resource?\n");
+ return irq;
+ }
+
+ spin_lock_init(&qspi->lock);
+ mutex_init(&qspi->list_lock);
+
+ qspi->base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(qspi->base)) {
+ ret = PTR_ERR(qspi->base);
+ goto free_master;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, irq, ti_qspi_isr,
+ ti_qspi_threaded_isr, 0,
+ dev_name(&pdev->dev), qspi);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register ISR for IRQ %d\n",
+ irq);
+ goto free_master;
+ }
+
+ qspi->fclk = devm_clk_get(&pdev->dev, "fck");
+ if (IS_ERR(qspi->fclk)) {
+ ret = PTR_ERR(qspi->fclk);
+ dev_err(&pdev->dev, "could not get clk: %d\n", ret);
+ }
+
+ init_completion(&qspi->transfer_complete);
+
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, QSPI_AUTOSUSPEND_TIMEOUT);
+ pm_runtime_enable(&pdev->dev);
+
+ if (!of_property_read_u32(np, "spi-max-frequency", &max_freq))
+ qspi->spi_max_frequency = max_freq;
+
+ ret = spi_register_master(master);
+ if (ret)
+ goto free_master;
+
+ return 0;
+
+free_master:
+ spi_master_put(master);
+ return ret;
+}
+
+static int ti_qspi_remove(struct platform_device *pdev)
+{
+ struct ti_qspi *qspi = platform_get_drvdata(pdev);
+
+ spi_unregister_master(qspi->master);
+
+ return 0;
+}
+
+static const struct dev_pm_ops ti_qspi_pm_ops = {
+ .runtime_resume = ti_qspi_runtime_resume,
+};
+
+static struct platform_driver ti_qspi_driver = {
+ .probe = ti_qspi_probe,
+ .remove = ti_qspi_remove,
+ .driver = {
+ .name = "ti,dra7xxx-qspi",
+ .owner = THIS_MODULE,
+ .pm = &ti_qspi_pm_ops,
+ .of_match_table = ti_qspi_match,
+ }
+};
+
+module_platform_driver(ti_qspi_driver);
+
+MODULE_AUTHOR("Sourav Poddar <sourav.poddar@ti.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("TI QSPI controller driver");
struct device *dev = &pdev->dev;
int error = 0;
- pdata = dev->platform_data;
+ pdata = dev_get_platdata(dev);
if (!pdata) {
dev_err(dev, "platform data not found\n");
return -EINVAL;
buff[1] = gpio_state;
}
- dev_dbg(&st->us->dev, "buff %02x,%02x,%02x\n",
- buff[0], buff[1], buff[2]);
+ dev_dbg(&st->us->dev, "buff %3ph\n", buff);
return spi_write(st->us, buff, (st->nr_gpio == 16) ? 3 : 2);
}
int ptr;
int ret;
- pdata = spi->dev.platform_data;
+ pdata = dev_get_platdata(&spi->dev);
if (pdata == NULL) {
dev_err(&spi->dev, "no device data specified\n");
return -EINVAL;
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Intel EG20T PCH/LAPIS Semiconductor ML7xxx IOH SPI Driver");
+MODULE_DEVICE_TABLE(pci, pch_spi_pcidev_id);
+
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
-#include <asm/gpio.h>
+#include <linux/gpio.h>
#define SPI_FIFO_SIZE 4
/* bitbang has to be first */
struct spi_bitbang bitbang;
struct completion done;
- struct resource mem; /* phys mem */
void __iomem *regs; /* virt. address of the control registers */
- u32 irq;
+ int irq;
u8 *rx_ptr; /* pointer in the Tx buffer */
const u8 *tx_ptr; /* pointer in the Rx buffer */
return 0;
}
-static int xilinx_spi_setup(struct spi_device *spi)
-{
- /* always return 0, we can not check the number of bits.
- * There are cases when SPI setup is called before any driver is
- * there, in that case the SPI core defaults to 8 bits, which we
- * do not support in some cases. But if we return an error, the
- * SPI device would not be registered and no driver can get hold of it
- * When the driver is there, it will call SPI setup again with the
- * correct number of bits per transfer.
- * If a driver setups with the wrong bit number, it will fail when
- * it tries to do a transfer
- */
- return 0;
-}
-
static void xilinx_spi_fill_tx_fifo(struct xilinx_spi *xspi)
{
u8 sr;
};
MODULE_DEVICE_TABLE(of, xilinx_spi_of_match);
-struct spi_master *xilinx_spi_init(struct device *dev, struct resource *mem,
- u32 irq, s16 bus_num, int num_cs, int bits_per_word)
+static int xilinx_spi_probe(struct platform_device *pdev)
{
- struct spi_master *master;
struct xilinx_spi *xspi;
- int ret;
+ struct xspi_platform_data *pdata;
+ struct resource *res;
+ int ret, num_cs = 0, bits_per_word = 8;
+ struct spi_master *master;
u32 tmp;
+ u8 i;
+
+ pdata = dev_get_platdata(&pdev->dev);
+ if (pdata) {
+ num_cs = pdata->num_chipselect;
+ bits_per_word = pdata->bits_per_word;
+ } else {
+ of_property_read_u32(pdev->dev.of_node, "xlnx,num-ss-bits",
+ &num_cs);
+ }
+
+ if (!num_cs) {
+ dev_err(&pdev->dev,
+ "Missing slave select configuration data\n");
+ return -EINVAL;
+ }
- master = spi_alloc_master(dev, sizeof(struct xilinx_spi));
+ master = spi_alloc_master(&pdev->dev, sizeof(struct xilinx_spi));
if (!master)
- return NULL;
+ return -ENODEV;
/* the spi->mode bits understood by this driver: */
master->mode_bits = SPI_CPOL | SPI_CPHA;
xspi->bitbang.chipselect = xilinx_spi_chipselect;
xspi->bitbang.setup_transfer = xilinx_spi_setup_transfer;
xspi->bitbang.txrx_bufs = xilinx_spi_txrx_bufs;
- xspi->bitbang.master->setup = xilinx_spi_setup;
init_completion(&xspi->done);
- if (!request_mem_region(mem->start, resource_size(mem),
- XILINX_SPI_NAME))
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ xspi->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(xspi->regs)) {
+ ret = PTR_ERR(xspi->regs);
goto put_master;
-
- xspi->regs = ioremap(mem->start, resource_size(mem));
- if (xspi->regs == NULL) {
- dev_warn(dev, "ioremap failure\n");
- goto map_failed;
}
- master->bus_num = bus_num;
+ master->bus_num = pdev->dev.id;
master->num_chipselect = num_cs;
- master->dev.of_node = dev->of_node;
-
- xspi->mem = *mem;
- xspi->irq = irq;
+ master->dev.of_node = pdev->dev.of_node;
/*
* Detect endianess on the IP via loop bit in CR. Detection
} else if (xspi->bits_per_word == 32) {
xspi->tx_fn = xspi_tx32;
xspi->rx_fn = xspi_rx32;
- } else
- goto unmap_io;
-
+ } else {
+ ret = -EINVAL;
+ goto put_master;
+ }
/* SPI controller initializations */
xspi_init_hw(xspi);
+ xspi->irq = platform_get_irq(pdev, 0);
+ if (xspi->irq < 0) {
+ ret = xspi->irq;
+ goto put_master;
+ }
+
/* Register for SPI Interrupt */
- ret = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi);
+ ret = devm_request_irq(&pdev->dev, xspi->irq, xilinx_spi_irq, 0,
+ dev_name(&pdev->dev), xspi);
if (ret)
- goto unmap_io;
+ goto put_master;
ret = spi_bitbang_start(&xspi->bitbang);
if (ret) {
- dev_err(dev, "spi_bitbang_start FAILED\n");
- goto free_irq;
- }
-
- dev_info(dev, "at 0x%08llX mapped to 0x%p, irq=%d\n",
- (unsigned long long)mem->start, xspi->regs, xspi->irq);
- return master;
-
-free_irq:
- free_irq(xspi->irq, xspi);
-unmap_io:
- iounmap(xspi->regs);
-map_failed:
- release_mem_region(mem->start, resource_size(mem));
-put_master:
- spi_master_put(master);
- return NULL;
-}
-EXPORT_SYMBOL(xilinx_spi_init);
-
-void xilinx_spi_deinit(struct spi_master *master)
-{
- struct xilinx_spi *xspi;
-
- xspi = spi_master_get_devdata(master);
-
- spi_bitbang_stop(&xspi->bitbang);
- free_irq(xspi->irq, xspi);
- iounmap(xspi->regs);
-
- release_mem_region(xspi->mem.start, resource_size(&xspi->mem));
- spi_master_put(xspi->bitbang.master);
-}
-EXPORT_SYMBOL(xilinx_spi_deinit);
-
-static int xilinx_spi_probe(struct platform_device *dev)
-{
- struct xspi_platform_data *pdata;
- struct resource *r;
- int irq, num_cs = 0, bits_per_word = 8;
- struct spi_master *master;
- u8 i;
-
- pdata = dev->dev.platform_data;
- if (pdata) {
- num_cs = pdata->num_chipselect;
- bits_per_word = pdata->bits_per_word;
- }
-
-#ifdef CONFIG_OF
- if (dev->dev.of_node) {
- const __be32 *prop;
- int len;
-
- /* number of slave select bits is required */
- prop = of_get_property(dev->dev.of_node, "xlnx,num-ss-bits",
- &len);
- if (prop && len >= sizeof(*prop))
- num_cs = __be32_to_cpup(prop);
- }
-#endif
-
- if (!num_cs) {
- dev_err(&dev->dev, "Missing slave select configuration data\n");
- return -EINVAL;
+ dev_err(&pdev->dev, "spi_bitbang_start FAILED\n");
+ goto put_master;
}
-
- r = platform_get_resource(dev, IORESOURCE_MEM, 0);
- if (!r)
- return -ENODEV;
-
- irq = platform_get_irq(dev, 0);
- if (irq < 0)
- return -ENXIO;
-
- master = xilinx_spi_init(&dev->dev, r, irq, dev->id, num_cs,
- bits_per_word);
- if (!master)
- return -ENODEV;
+ dev_info(&pdev->dev, "at 0x%08llX mapped to 0x%p, irq=%d\n",
+ (unsigned long long)res->start, xspi->regs, xspi->irq);
if (pdata) {
for (i = 0; i < pdata->num_devices; i++)
spi_new_device(master, pdata->devices + i);
}
- platform_set_drvdata(dev, master);
+ platform_set_drvdata(pdev, master);
return 0;
+
+put_master:
+ spi_master_put(master);
+
+ return ret;
}
-static int xilinx_spi_remove(struct platform_device *dev)
+static int xilinx_spi_remove(struct platform_device *pdev)
{
- xilinx_spi_deinit(platform_get_drvdata(dev));
+ struct spi_master *master = platform_get_drvdata(pdev);
+ struct xilinx_spi *xspi = spi_master_get_devdata(master);
+ void __iomem *regs_base = xspi->regs;
+
+ spi_bitbang_stop(&xspi->bitbang);
+
+ /* Disable all the interrupts just in case */
+ xspi->write_fn(0, regs_base + XIPIF_V123B_IIER_OFFSET);
+ /* Disable the global IPIF interrupt */
+ xspi->write_fn(0, regs_base + XIPIF_V123B_DGIER_OFFSET);
+
+ spi_master_put(xspi->bitbang.master);
return 0;
}
master->unprepare_transfer_hardware(master))
dev_err(&master->dev,
"failed to unprepare transfer hardware\n");
+ if (master->auto_runtime_pm) {
+ pm_runtime_mark_last_busy(master->dev.parent);
+ pm_runtime_put_autosuspend(master->dev.parent);
+ }
return;
}
master->busy = true;
spin_unlock_irqrestore(&master->queue_lock, flags);
+ if (!was_busy && master->auto_runtime_pm) {
+ ret = pm_runtime_get_sync(master->dev.parent);
+ if (ret < 0) {
+ dev_err(&master->dev, "Failed to power device: %d\n",
+ ret);
+ return;
+ }
+ }
+
if (!was_busy && master->prepare_transfer_hardware) {
ret = master->prepare_transfer_hardware(master);
if (ret) {
dev_err(&master->dev,
"failed to prepare transfer hardware\n");
+
+ if (master->auto_runtime_pm)
+ pm_runtime_put(master->dev.parent);
return;
}
}
msg->status = -EINPROGRESS;
list_add_tail(&msg->queue, &master->queue);
- if (master->running && !master->busy)
+ if (!master->busy)
queue_kthread_work(&master->kworker, &master->pump_messages);
spin_unlock_irqrestore(&master->queue_lock, flags);
if (of_find_property(nc, "spi-3wire", NULL))
spi->mode |= SPI_3WIRE;
+ /* Device DUAL/QUAD mode */
+ prop = of_get_property(nc, "spi-tx-bus-width", &len);
+ if (prop && len == sizeof(*prop)) {
+ switch (be32_to_cpup(prop)) {
+ case SPI_NBITS_SINGLE:
+ break;
+ case SPI_NBITS_DUAL:
+ spi->mode |= SPI_TX_DUAL;
+ break;
+ case SPI_NBITS_QUAD:
+ spi->mode |= SPI_TX_QUAD;
+ break;
+ default:
+ dev_err(&master->dev,
+ "spi-tx-bus-width %d not supported\n",
+ be32_to_cpup(prop));
+ spi_dev_put(spi);
+ continue;
+ }
+ }
+
+ prop = of_get_property(nc, "spi-rx-bus-width", &len);
+ if (prop && len == sizeof(*prop)) {
+ switch (be32_to_cpup(prop)) {
+ case SPI_NBITS_SINGLE:
+ break;
+ case SPI_NBITS_DUAL:
+ spi->mode |= SPI_RX_DUAL;
+ break;
+ case SPI_NBITS_QUAD:
+ spi->mode |= SPI_RX_QUAD;
+ break;
+ default:
+ dev_err(&master->dev,
+ "spi-rx-bus-width %d not supported\n",
+ be32_to_cpup(prop));
+ spi_dev_put(spi);
+ continue;
+ }
+ }
+
/* Device speed */
prop = of_get_property(nc, "spi-max-frequency", &len);
if (!prop || len < sizeof(*prop)) {
else {
status = spi_master_initialize_queue(master);
if (status) {
- device_unregister(&master->dev);
+ device_del(&master->dev);
goto done;
}
}
unsigned bad_bits;
int status = 0;
+ /* check mode to prevent that DUAL and QUAD set at the same time
+ */
+ if (((spi->mode & SPI_TX_DUAL) && (spi->mode & SPI_TX_QUAD)) ||
+ ((spi->mode & SPI_RX_DUAL) && (spi->mode & SPI_RX_QUAD))) {
+ dev_err(&spi->dev,
+ "setup: can not select dual and quad at the same time\n");
+ return -EINVAL;
+ }
+ /* if it is SPI_3WIRE mode, DUAL and QUAD should be forbidden
+ */
+ if ((spi->mode & SPI_3WIRE) && (spi->mode &
+ (SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD)))
+ return -EINVAL;
/* help drivers fail *cleanly* when they need options
* that aren't supported with their current master
*/
struct spi_master *master = spi->master;
struct spi_transfer *xfer;
+ if (list_empty(&message->transfers))
+ return -EINVAL;
+ if (!message->complete)
+ return -EINVAL;
+
/* Half-duplex links include original MicroWire, and ones with
* only one data pin like SPI_3WIRE (switches direction) or where
* either MOSI or MISO is missing. They can also be caused by
/**
* Set transfer bits_per_word and max speed as spi device default if
* it is not set for this transfer.
+ * Set transfer tx_nbits and rx_nbits as single transfer default
+ * (SPI_NBITS_SINGLE) if it is not set for this transfer.
*/
list_for_each_entry(xfer, &message->transfers, transfer_list) {
+ message->frame_length += xfer->len;
if (!xfer->bits_per_word)
xfer->bits_per_word = spi->bits_per_word;
- if (!xfer->speed_hz)
+ if (!xfer->speed_hz) {
xfer->speed_hz = spi->max_speed_hz;
+ if (master->max_speed_hz &&
+ xfer->speed_hz > master->max_speed_hz)
+ xfer->speed_hz = master->max_speed_hz;
+ }
+
if (master->bits_per_word_mask) {
/* Only 32 bits fit in the mask */
if (xfer->bits_per_word > 32)
BIT(xfer->bits_per_word - 1)))
return -EINVAL;
}
+
+ if (xfer->speed_hz && master->min_speed_hz &&
+ xfer->speed_hz < master->min_speed_hz)
+ return -EINVAL;
+ if (xfer->speed_hz && master->max_speed_hz &&
+ xfer->speed_hz > master->max_speed_hz)
+ return -EINVAL;
+
+ if (xfer->tx_buf && !xfer->tx_nbits)
+ xfer->tx_nbits = SPI_NBITS_SINGLE;
+ if (xfer->rx_buf && !xfer->rx_nbits)
+ xfer->rx_nbits = SPI_NBITS_SINGLE;
+ /* check transfer tx/rx_nbits:
+ * 1. keep the value is not out of single, dual and quad
+ * 2. keep tx/rx_nbits is contained by mode in spi_device
+ * 3. if SPI_3WIRE, tx/rx_nbits should be in single
+ */
+ if (xfer->tx_buf) {
+ if (xfer->tx_nbits != SPI_NBITS_SINGLE &&
+ xfer->tx_nbits != SPI_NBITS_DUAL &&
+ xfer->tx_nbits != SPI_NBITS_QUAD)
+ return -EINVAL;
+ if ((xfer->tx_nbits == SPI_NBITS_DUAL) &&
+ !(spi->mode & (SPI_TX_DUAL | SPI_TX_QUAD)))
+ return -EINVAL;
+ if ((xfer->tx_nbits == SPI_NBITS_QUAD) &&
+ !(spi->mode & SPI_TX_QUAD))
+ return -EINVAL;
+ if ((spi->mode & SPI_3WIRE) &&
+ (xfer->tx_nbits != SPI_NBITS_SINGLE))
+ return -EINVAL;
+ }
+ /* check transfer rx_nbits */
+ if (xfer->rx_buf) {
+ if (xfer->rx_nbits != SPI_NBITS_SINGLE &&
+ xfer->rx_nbits != SPI_NBITS_DUAL &&
+ xfer->rx_nbits != SPI_NBITS_QUAD)
+ return -EINVAL;
+ if ((xfer->rx_nbits == SPI_NBITS_DUAL) &&
+ !(spi->mode & (SPI_RX_DUAL | SPI_RX_QUAD)))
+ return -EINVAL;
+ if ((xfer->rx_nbits == SPI_NBITS_QUAD) &&
+ !(spi->mode & SPI_RX_QUAD))
+ return -EINVAL;
+ if ((spi->mode & SPI_3WIRE) &&
+ (xfer->rx_nbits != SPI_NBITS_SINGLE))
+ return -EINVAL;
+ }
}
message->spi = spi;
ret = comedi_device_postconfig(dev);
if (ret < 0) {
comedi_device_detach(dev);
- module_put(dev->driver->module);
+ module_put(driv->module);
}
/* On success, the driver module count has been incremented. */
return ret;
pr_devel("HVSI@%x: ... waiting handshake\n", pv->termno);
- /* Try for up to 200s */
- for (timeout = 0; timeout < 20; timeout++) {
+ /* Try for up to 400ms */
+ for (timeout = 0; timeout < 40; timeout++) {
if (pv->established)
goto established;
if (!hvsi_get_packet(pv))
pr_info("%s: " DRIVER_DESC "\n", hcd_name);
ohci_init_driver(&ohci_pci_hc_driver, &pci_overrides);
+
+#ifdef CONFIG_PM
+ /* Entries for the PCI suspend/resume callbacks are special */
+ ohci_pci_hc_driver.pci_suspend = ohci_suspend;
+ ohci_pci_hc_driver.pci_resume = ohci_resume;
+#endif
+
return pci_register_driver(&ohci_pci_driver);
}
module_init(ohci_pci_init);
for_each_possible_cpu(i)
memset(per_cpu(cpu_evtchn_mask, i),
- (i == 0) ? ~0 : 0, sizeof(*per_cpu(cpu_evtchn_mask, i)));
+ (i == 0) ? ~0 : 0, NR_EVENT_CHANNELS/8);
}
static inline void clear_evtchn(int port)
/* Rebind an evtchn so that it gets delivered to a specific cpu */
static int rebind_irq_to_cpu(unsigned irq, unsigned tcpu)
{
+ struct shared_info *s = HYPERVISOR_shared_info;
struct evtchn_bind_vcpu bind_vcpu;
int evtchn = evtchn_from_irq(irq);
+ int masked;
if (!VALID_EVTCHN(evtchn))
return -1;
bind_vcpu.port = evtchn;
bind_vcpu.vcpu = tcpu;
+ /*
+ * Mask the event while changing the VCPU binding to prevent
+ * it being delivered on an unexpected VCPU.
+ */
+ masked = sync_test_and_set_bit(evtchn, BM(s->evtchn_mask));
+
/*
* If this fails, it usually just indicates that we're dealing with a
* virq or IPI channel, which don't actually need to be rebound. Ignore
if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_vcpu, &bind_vcpu) >= 0)
bind_evtchn_to_cpu(evtchn, tcpu);
+ if (!masked)
+ unmask_evtchn(evtchn);
+
return 0;
}
int block, off;
inode = iget_locked(sb, ino);
- if (IS_ERR(inode))
+ if (!inode)
return ERR_PTR(-ENOMEM);
if (!(inode->i_state & I_NEW))
return inode;
int bio_uncopy_user(struct bio *bio)
{
struct bio_map_data *bmd = bio->bi_private;
- int ret = 0;
+ struct bio_vec *bvec;
+ int ret = 0, i;
- if (!bio_flagged(bio, BIO_NULL_MAPPED))
- ret = __bio_copy_iov(bio, bmd->iovecs, bmd->sgvecs,
- bmd->nr_sgvecs, bio_data_dir(bio) == READ,
- 0, bmd->is_our_pages);
+ if (!bio_flagged(bio, BIO_NULL_MAPPED)) {
+ /*
+ * if we're in a workqueue, the request is orphaned, so
+ * don't copy into a random user address space, just free.
+ */
+ if (current->mm)
+ ret = __bio_copy_iov(bio, bmd->iovecs, bmd->sgvecs,
+ bmd->nr_sgvecs, bio_data_dir(bio) == READ,
+ 0, bmd->is_our_pages);
+ else if (bmd->is_our_pages)
+ bio_for_each_segment_all(bvec, bio, i)
+ __free_page(bvec->bv_page);
+ }
bio_free_map_data(bmd);
bio_put(bio);
return ret;
*/
static void d_free(struct dentry *dentry)
{
- BUG_ON(dentry->d_count);
+ BUG_ON(dentry->d_lockref.count);
this_cpu_dec(nr_dentry);
if (dentry->d_op && dentry->d_op->d_release)
dentry->d_op->d_release(dentry);
}
if (ref)
- dentry->d_count--;
+ dentry->d_lockref.count--;
/*
* inform the fs via d_prune that this dentry is about to be
* unhashed and destroyed.
return;
repeat:
- if (dentry->d_count == 1)
+ if (dentry->d_lockref.count == 1)
might_sleep();
- spin_lock(&dentry->d_lock);
- BUG_ON(!dentry->d_count);
- if (dentry->d_count > 1) {
- dentry->d_count--;
- spin_unlock(&dentry->d_lock);
+ if (lockref_put_or_lock(&dentry->d_lockref))
return;
- }
if (dentry->d_flags & DCACHE_OP_DELETE) {
if (dentry->d_op->d_delete(dentry))
dentry->d_flags |= DCACHE_REFERENCED;
dentry_lru_add(dentry);
- dentry->d_count--;
+ dentry->d_lockref.count--;
spin_unlock(&dentry->d_lock);
return;
* We also need to leave mountpoints alone,
* directory or not.
*/
- if (dentry->d_count > 1 && dentry->d_inode) {
+ if (dentry->d_lockref.count > 1 && dentry->d_inode) {
if (S_ISDIR(dentry->d_inode->i_mode) || d_mountpoint(dentry)) {
spin_unlock(&dentry->d_lock);
return -EBUSY;
/* This must be called with d_lock held */
static inline void __dget_dlock(struct dentry *dentry)
{
- dentry->d_count++;
+ dentry->d_lockref.count++;
}
static inline void __dget(struct dentry *dentry)
{
- spin_lock(&dentry->d_lock);
- __dget_dlock(dentry);
- spin_unlock(&dentry->d_lock);
+ lockref_get(&dentry->d_lockref);
}
struct dentry *dget_parent(struct dentry *dentry)
{
+ int gotref;
struct dentry *ret;
+ /*
+ * Do optimistic parent lookup without any
+ * locking.
+ */
+ rcu_read_lock();
+ ret = ACCESS_ONCE(dentry->d_parent);
+ gotref = lockref_get_not_zero(&ret->d_lockref);
+ rcu_read_unlock();
+ if (likely(gotref)) {
+ if (likely(ret == ACCESS_ONCE(dentry->d_parent)))
+ return ret;
+ dput(ret);
+ }
+
repeat:
/*
* Don't need rcu_dereference because we re-check it was correct under
goto repeat;
}
rcu_read_unlock();
- BUG_ON(!ret->d_count);
- ret->d_count++;
+ BUG_ON(!ret->d_lockref.count);
+ ret->d_lockref.count++;
spin_unlock(&ret->d_lock);
return ret;
}
spin_lock(&inode->i_lock);
hlist_for_each_entry(dentry, &inode->i_dentry, d_alias) {
spin_lock(&dentry->d_lock);
- if (!dentry->d_count) {
+ if (!dentry->d_lockref.count) {
__dget_dlock(dentry);
__d_drop(dentry);
spin_unlock(&dentry->d_lock);
/* Prune ancestors. */
dentry = parent;
while (dentry) {
- spin_lock(&dentry->d_lock);
- if (dentry->d_count > 1) {
- dentry->d_count--;
- spin_unlock(&dentry->d_lock);
+ if (lockref_put_or_lock(&dentry->d_lockref))
return;
- }
dentry = dentry_kill(dentry, 1);
}
}
* the LRU because of laziness during lookup. Do not free
* it - just keep it off the LRU list.
*/
- if (dentry->d_count) {
+ if (dentry->d_lockref.count) {
dentry_lru_del(dentry);
spin_unlock(&dentry->d_lock);
continue;
dentry_lru_del(dentry);
__d_shrink(dentry);
- if (dentry->d_count != 0) {
+ if (dentry->d_lockref.count != 0) {
printk(KERN_ERR
"BUG: Dentry %p{i=%lx,n=%s}"
" still in use (%d)"
dentry->d_inode ?
dentry->d_inode->i_ino : 0UL,
dentry->d_name.name,
- dentry->d_count,
+ dentry->d_lockref.count,
dentry->d_sb->s_type->name,
dentry->d_sb->s_id);
BUG();
list_del(&dentry->d_u.d_child);
} else {
parent = dentry->d_parent;
- parent->d_count--;
+ parent->d_lockref.count--;
list_del(&dentry->d_u.d_child);
}
dentry = sb->s_root;
sb->s_root = NULL;
- dentry->d_count--;
+ dentry->d_lockref.count--;
shrink_dcache_for_umount_subtree(dentry);
while (!hlist_bl_empty(&sb->s_anon)) {
* loop in shrink_dcache_parent() might not make any progress
* and loop forever.
*/
- if (dentry->d_count) {
+ if (dentry->d_lockref.count) {
dentry_lru_del(dentry);
} else if (!(dentry->d_flags & DCACHE_SHRINK_LIST)) {
dentry_lru_move_list(dentry, dispose);
smp_wmb();
dentry->d_name.name = dname;
- dentry->d_count = 1;
+ dentry->d_lockref.count = 1;
dentry->d_flags = 0;
spin_lock_init(&dentry->d_lock);
seqcount_init(&dentry->d_seq);
* without taking d_lock and checking d_seq sequence count against @seq
* returned here.
*
- * A refcount may be taken on the found dentry with the __d_rcu_to_refcount
+ * A refcount may be taken on the found dentry with the d_rcu_to_refcount
* function.
*
* Alternatively, __d_lookup_rcu may be called again to look up the child of
goto next;
}
- dentry->d_count++;
+ dentry->d_lockref.count++;
found = dentry;
spin_unlock(&dentry->d_lock);
break;
spin_lock(&dentry->d_lock);
inode = dentry->d_inode;
isdir = S_ISDIR(inode->i_mode);
- if (dentry->d_count == 1) {
+ if (dentry->d_lockref.count == 1) {
if (!spin_trylock(&inode->i_lock)) {
spin_unlock(&dentry->d_lock);
cpu_relax();
return memcpy(buffer, temp, sz);
}
+char *simple_dname(struct dentry *dentry, char *buffer, int buflen)
+{
+ char *end = buffer + buflen;
+ /* these dentries are never renamed, so d_lock is not needed */
+ if (prepend(&end, &buflen, " (deleted)", 11) ||
+ prepend_name(&end, &buflen, &dentry->d_name) ||
+ prepend(&end, &buflen, "/", 1))
+ end = ERR_PTR(-ENAMETOOLONG);
+ return end;
+}
+
/*
* Write full pathname from the root of the filesystem into the buffer.
*/
}
if (!(dentry->d_flags & DCACHE_GENOCIDE)) {
dentry->d_flags |= DCACHE_GENOCIDE;
- dentry->d_count--;
+ dentry->d_lockref.count--;
}
spin_unlock(&dentry->d_lock);
}
struct dentry *child = this_parent;
if (!(this_parent->d_flags & DCACHE_GENOCIDE)) {
this_parent->d_flags |= DCACHE_GENOCIDE;
- this_parent->d_count--;
+ this_parent->d_lockref.count--;
}
this_parent = try_to_ascend(this_parent, locked, seq);
if (!this_parent)
struct inode *inode;
inode = iget_locked(super, ino);
- if (IS_ERR(inode))
+ if (!inode)
return ERR_PTR(-ENOMEM);
if (!(inode->i_state & I_NEW))
return inode;
glock_workqueue = alloc_workqueue("glock_workqueue", WQ_MEM_RECLAIM |
WQ_HIGHPRI | WQ_FREEZABLE, 0);
- if (IS_ERR(glock_workqueue))
- return PTR_ERR(glock_workqueue);
+ if (!glock_workqueue)
+ return -ENOMEM;
gfs2_delete_workqueue = alloc_workqueue("delete_workqueue",
WQ_MEM_RECLAIM | WQ_FREEZABLE,
0);
- if (IS_ERR(gfs2_delete_workqueue)) {
+ if (!gfs2_delete_workqueue) {
destroy_workqueue(glock_workqueue);
- return PTR_ERR(gfs2_delete_workqueue);
+ return -ENOMEM;
}
register_shrinker(&glock_shrinker);
* None of the buffers should be dirty, locked, or pinned.
*/
-static void __gfs2_ail_flush(struct gfs2_glock *gl, bool fsync)
+static void __gfs2_ail_flush(struct gfs2_glock *gl, bool fsync,
+ unsigned int nr_revokes)
{
struct gfs2_sbd *sdp = gl->gl_sbd;
struct list_head *head = &gl->gl_ail_list;
gfs2_log_lock(sdp);
spin_lock(&sdp->sd_ail_lock);
- list_for_each_entry_safe(bd, tmp, head, bd_ail_gl_list) {
+ list_for_each_entry_safe_reverse(bd, tmp, head, bd_ail_gl_list) {
+ if (nr_revokes == 0)
+ break;
bh = bd->bd_bh;
if (bh->b_state & b_state) {
if (fsync)
gfs2_ail_error(gl, bh);
}
gfs2_trans_add_revoke(sdp, bd);
+ nr_revokes--;
}
GLOCK_BUG_ON(gl, !fsync && atomic_read(&gl->gl_ail_count));
spin_unlock(&sdp->sd_ail_lock);
WARN_ON_ONCE(current->journal_info);
current->journal_info = &tr;
- __gfs2_ail_flush(gl, 0);
+ __gfs2_ail_flush(gl, 0, tr.tr_revokes);
gfs2_trans_end(sdp);
gfs2_log_flush(sdp, NULL);
{
struct gfs2_sbd *sdp = gl->gl_sbd;
unsigned int revokes = atomic_read(&gl->gl_ail_count);
+ unsigned int max_revokes = (sdp->sd_sb.sb_bsize - sizeof(struct gfs2_log_descriptor)) / sizeof(u64);
int ret;
if (!revokes)
return;
- ret = gfs2_trans_begin(sdp, 0, revokes);
+ while (revokes > max_revokes)
+ max_revokes += (sdp->sd_sb.sb_bsize - sizeof(struct gfs2_meta_header)) / sizeof(u64);
+
+ ret = gfs2_trans_begin(sdp, 0, max_revokes);
if (ret)
return;
- __gfs2_ail_flush(gl, fsync);
+ __gfs2_ail_flush(gl, fsync, max_revokes);
gfs2_trans_end(sdp);
gfs2_log_flush(sdp, NULL);
}
}
gfs2_glock_dq_uninit(ghs);
if (IS_ERR(d))
- return PTR_RET(d);
+ return PTR_ERR(d);
return error;
} else if (error != -ENOENT) {
goto fail_gunlock;
struct gfs2_holder gh;
int ret;
+ /* For selinux during lookup */
+ if (gfs2_glock_is_locked_by_me(ip->i_gl))
+ return generic_getxattr(dentry, name, data, size);
+
gfs2_holder_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &gh);
ret = gfs2_glock_nq(&gh);
if (ret == 0) {
goto fail_wq;
gfs2_control_wq = alloc_workqueue("gfs2_control",
- WQ_NON_REENTRANT | WQ_UNBOUND | WQ_FREEZABLE, 0);
+ WQ_UNBOUND | WQ_FREEZABLE, 0);
if (!gfs2_control_wq)
goto fail_recovery;
return h - hstates;
}
-static char *hugetlb_dname(struct dentry *dentry, char *buffer, int buflen)
-{
- return dynamic_dname(dentry, buffer, buflen, "/%s (deleted)",
- dentry->d_name.name);
-}
-
static struct dentry_operations anon_ops = {
- .d_dname = hugetlb_dname
+ .d_dname = simple_dname
};
/*
dir_index = (u32) ctx->pos;
+ /*
+ * NFSv4 reserves cookies 1 and 2 for . and .. so the value
+ * we return to the vfs is one greater than the one we use
+ * internally.
+ */
+ if (dir_index)
+ dir_index--;
+
if (dir_index > 1) {
struct dir_table_slot dirtab_slot;
if (p->header.flag & BT_INTERNAL) {
jfs_err("jfs_readdir: bad index table");
DT_PUTPAGE(mp);
- ctx->pos = -1;
+ ctx->pos = DIREND;
return 0;
}
} else {
/*
* self "."
*/
- ctx->pos = 0;
+ ctx->pos = 1;
if (!dir_emit(ctx, ".", 1, ip->i_ino, DT_DIR))
return 0;
}
/*
* parent ".."
*/
- ctx->pos = 1;
+ ctx->pos = 2;
if (!dir_emit(ctx, "..", 2, PARENT(ip), DT_DIR))
return 0;
/*
* Legacy filesystem - OS/2 & Linux JFS < 0.3.6
*
- * pn = index = 0: First entry "."
- * pn = 0; index = 1: Second entry ".."
+ * pn = 0; index = 1: First entry "."
+ * pn = 0; index = 2: Second entry ".."
* pn > 0: Real entries, pn=1 -> leftmost page
* pn = index = -1: No more entries
*/
dtpos = ctx->pos;
- if (dtpos == 0) {
+ if (dtpos < 2) {
/* build "." entry */
+ ctx->pos = 1;
if (!dir_emit(ctx, ".", 1, ip->i_ino, DT_DIR))
return 0;
- dtoffset->index = 1;
+ dtoffset->index = 2;
ctx->pos = dtpos;
}
if (dtoffset->pn == 0) {
- if (dtoffset->index == 1) {
+ if (dtoffset->index == 2) {
/* build ".." entry */
if (!dir_emit(ctx, "..", 2, PARENT(ip), DT_DIR))
return 0;
}
jfs_dirent->position = unique_pos++;
}
+ /*
+ * We add 1 to the index because we may
+ * use a value of 2 internally, and NFSv4
+ * doesn't like that.
+ */
+ jfs_dirent->position++;
} else {
jfs_dirent->position = dtpos;
len = min(d_namleft, DTLHDRDATALEN_LEGACY);
br_read_unlock(&vfsmount_lock);
}
+/*
+ * When we move over from the RCU domain to properly refcounted
+ * long-lived dentries, we need to check the sequence numbers
+ * we got before lookup very carefully.
+ *
+ * We cannot blindly increment a dentry refcount - even if it
+ * is not locked - if it is zero, because it may have gone
+ * through the final d_kill() logic already.
+ *
+ * So for a zero refcount, we need to get the spinlock (which is
+ * safe even for a dead dentry because the de-allocation is
+ * RCU-delayed), and check the sequence count under the lock.
+ *
+ * Once we have checked the sequence count, we know it is live,
+ * and since we hold the spinlock it cannot die from under us.
+ *
+ * In contrast, if the reference count wasn't zero, we can just
+ * increment the lockref without having to take the spinlock.
+ * Even if the sequence number ends up being stale, we haven't
+ * gone through the final dput() and killed the dentry yet.
+ */
+static inline int d_rcu_to_refcount(struct dentry *dentry, seqcount_t *validate, unsigned seq)
+{
+ int gotref;
+
+ gotref = lockref_get_or_lock(&dentry->d_lockref);
+
+ /* Does the sequence number still match? */
+ if (read_seqcount_retry(validate, seq)) {
+ if (gotref)
+ dput(dentry);
+ else
+ spin_unlock(&dentry->d_lock);
+ return -ECHILD;
+ }
+
+ /* Get the ref now, if we couldn't get it originally */
+ if (!gotref) {
+ dentry->d_lockref.count++;
+ spin_unlock(&dentry->d_lock);
+ }
+ return 0;
+}
+
/**
* unlazy_walk - try to switch to ref-walk mode.
* @nd: nameidata pathwalk data
nd->root.dentry != fs->root.dentry)
goto err_root;
}
- spin_lock(&parent->d_lock);
+
+ /*
+ * For a negative lookup, the lookup sequence point is the parents
+ * sequence point, and it only needs to revalidate the parent dentry.
+ *
+ * For a positive lookup, we need to move both the parent and the
+ * dentry from the RCU domain to be properly refcounted. And the
+ * sequence number in the dentry validates *both* dentry counters,
+ * since we checked the sequence number of the parent after we got
+ * the child sequence number. So we know the parent must still
+ * be valid if the child sequence number is still valid.
+ */
if (!dentry) {
- if (!__d_rcu_to_refcount(parent, nd->seq))
- goto err_parent;
+ if (d_rcu_to_refcount(parent, &parent->d_seq, nd->seq) < 0)
+ goto err_root;
BUG_ON(nd->inode != parent->d_inode);
} else {
- if (dentry->d_parent != parent)
+ if (d_rcu_to_refcount(dentry, &dentry->d_seq, nd->seq) < 0)
+ goto err_root;
+ if (d_rcu_to_refcount(parent, &dentry->d_seq, nd->seq) < 0)
goto err_parent;
- spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
- if (!__d_rcu_to_refcount(dentry, nd->seq))
- goto err_child;
- /*
- * If the sequence check on the child dentry passed, then
- * the child has not been removed from its parent. This
- * means the parent dentry must be valid and able to take
- * a reference at this point.
- */
- BUG_ON(!IS_ROOT(dentry) && dentry->d_parent != parent);
- BUG_ON(!parent->d_count);
- parent->d_count++;
- spin_unlock(&dentry->d_lock);
}
- spin_unlock(&parent->d_lock);
if (want_root) {
path_get(&nd->root);
spin_unlock(&fs->lock);
nd->flags &= ~LOOKUP_RCU;
return 0;
-err_child:
- spin_unlock(&dentry->d_lock);
err_parent:
- spin_unlock(&parent->d_lock);
+ dput(dentry);
err_root:
if (want_root)
spin_unlock(&fs->lock);
nd->flags &= ~LOOKUP_RCU;
if (!(nd->flags & LOOKUP_ROOT))
nd->root.mnt = NULL;
- spin_lock(&dentry->d_lock);
- if (unlikely(!__d_rcu_to_refcount(dentry, nd->seq))) {
- spin_unlock(&dentry->d_lock);
+
+ if (d_rcu_to_refcount(dentry, &dentry->d_seq, nd->seq) < 0) {
unlock_rcu_walk();
return -ECHILD;
}
- BUG_ON(nd->inode != dentry->d_inode);
- spin_unlock(&dentry->d_lock);
mntget(nd->path.mnt);
unlock_rcu_walk();
}
{
shrink_dcache_parent(dentry);
spin_lock(&dentry->d_lock);
- if (dentry->d_count == 1)
+ if (dentry->d_lockref.count == 1)
__d_drop(dentry);
spin_unlock(&dentry->d_lock);
}
if ((flags & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH)) != 0)
return -EINVAL;
/*
- * Using empty names is equivalent to using AT_SYMLINK_FOLLOW
- * on /proc/self/fd/<fd>.
+ * To use null names we require CAP_DAC_READ_SEARCH
+ * This ensures that not everyone will be able to create
+ * handlink using the passed filedescriptor.
*/
- if (flags & AT_EMPTY_PATH)
+ if (flags & AT_EMPTY_PATH) {
+ if (!capable(CAP_DAC_READ_SEARCH))
+ return -ENOENT;
how = LOOKUP_EMPTY;
+ }
if (flags & AT_SYMLINK_FOLLOW)
how |= LOOKUP_FOLLOW;
CL_COPY_ALL | CL_PRIVATE);
namespace_unlock();
if (IS_ERR(tree))
- return NULL;
+ return ERR_CAST(tree);
return &tree->mnt;
}
if (err == -EOPNOTSUPP) {
set_bit(BIO_EOPNOTSUPP, &bio->bi_flags);
- bio_put(bio);
- /* to be detected by submit_seg_bio() */
+ /* to be detected by nilfs_segbuf_submit_bio() */
}
if (!uptodate)
bio->bi_private = segbuf;
bio_get(bio);
submit_bio(mode, bio);
+ segbuf->sb_nbio++;
if (bio_flagged(bio, BIO_EOPNOTSUPP)) {
bio_put(bio);
err = -EOPNOTSUPP;
goto failed;
}
- segbuf->sb_nbio++;
bio_put(bio);
wi->bio = NULL;
struct inode *inode = NULL;
struct ocfs2_super *osb = NULL;
struct buffer_head *bh = NULL;
- char nodestr[8];
+ char nodestr[12];
struct ocfs2_blockcheck_stats stats;
trace_ocfs2_fill_super(sb, data, silent);
if (!p)
return -ENOENT;
- if (!dir_emit_dots(file, ctx))
- goto out;
if (!dir_emit_dots(file, ctx))
goto out;
files = get_files_struct(p);
de = next;
} while (de);
spin_unlock(&proc_subdir_lock);
- return 0;
+ return 1;
}
int proc_readdir(struct file *file, struct dir_context *ctx)
static int proc_root_readdir(struct file *file, struct dir_context *ctx)
{
if (ctx->pos < FIRST_PROCESS_ENTRY) {
- proc_readdir(file, ctx);
+ int error = proc_readdir(file, ctx);
+ if (unlikely(error <= 0))
+ return error;
ctx->pos = FIRST_PROCESS_ENTRY;
}
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
#endif
-#ifndef __HAVE_ARCH_PAGE_TEST_AND_CLEAR_YOUNG
-#define page_test_and_clear_young(pfn) (0)
-#endif
-
#ifndef __HAVE_ARCH_PGD_OFFSET_GATE
#define pgd_offset_gate(mm, addr) pgd_offset(mm, addr)
#endif
#include <linux/seqlock.h>
#include <linux/cache.h>
#include <linux/rcupdate.h>
+#include <linux/lockref.h>
struct nameidata;
struct path;
# endif
#endif
+#define d_lock d_lockref.lock
+
struct dentry {
/* RCU lookup touched fields */
unsigned int d_flags; /* protected by d_lock */
unsigned char d_iname[DNAME_INLINE_LEN]; /* small names */
/* Ref lookup also touches following */
- unsigned int d_count; /* protected by d_lock */
- spinlock_t d_lock; /* per dentry lock */
+ struct lockref d_lockref; /* per-dentry lock and refcount */
const struct dentry_operations *d_op;
struct super_block *d_sb; /* The root of the dentry tree */
unsigned long d_time; /* used by d_revalidate */
extern struct dentry *__d_lookup_rcu(const struct dentry *parent,
const struct qstr *name, unsigned *seq);
-/**
- * __d_rcu_to_refcount - take a refcount on dentry if sequence check is ok
- * @dentry: dentry to take a ref on
- * @seq: seqcount to verify against
- * Returns: 0 on failure, else 1.
- *
- * __d_rcu_to_refcount operates on a dentry,seq pair that was returned
- * by __d_lookup_rcu, to get a reference on an rcu-walk dentry.
- */
-static inline int __d_rcu_to_refcount(struct dentry *dentry, unsigned seq)
-{
- int ret = 0;
-
- assert_spin_locked(&dentry->d_lock);
- if (!read_seqcount_retry(&dentry->d_seq, seq)) {
- ret = 1;
- dentry->d_count++;
- }
-
- return ret;
-}
-
static inline unsigned d_count(const struct dentry *dentry)
{
- return dentry->d_count;
+ return dentry->d_lockref.count;
}
/* validate "insecure" dentry pointer */
* helper function for dentry_operations.d_dname() members
*/
extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...);
+extern char *simple_dname(struct dentry *, char *, int);
extern char *__d_path(const struct path *, const struct path *, char *, int);
extern char *d_absolute_path(const struct path *, char *, int);
static inline struct dentry *dget_dlock(struct dentry *dentry)
{
if (dentry)
- dentry->d_count++;
+ dentry->d_lockref.count++;
return dentry;
}
static inline struct dentry *dget(struct dentry *dentry)
{
- if (dentry) {
- spin_lock(&dentry->d_lock);
- dget_dlock(dentry);
- spin_unlock(&dentry->d_lock);
- }
+ if (dentry)
+ lockref_get(&dentry->d_lockref);
return dentry;
}
#include <linux/bitmap.h>
#include <linux/if.h>
+#include <linux/ip.h>
#include <linux/netdevice.h>
#include <linux/rcupdate.h>
#include <linux/timer.h>
#include <linux/sysctl.h>
#include <linux/rtnetlink.h>
-enum
-{
- IPV4_DEVCONF_FORWARDING=1,
- IPV4_DEVCONF_MC_FORWARDING,
- IPV4_DEVCONF_PROXY_ARP,
- IPV4_DEVCONF_ACCEPT_REDIRECTS,
- IPV4_DEVCONF_SECURE_REDIRECTS,
- IPV4_DEVCONF_SEND_REDIRECTS,
- IPV4_DEVCONF_SHARED_MEDIA,
- IPV4_DEVCONF_RP_FILTER,
- IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE,
- IPV4_DEVCONF_BOOTP_RELAY,
- IPV4_DEVCONF_LOG_MARTIANS,
- IPV4_DEVCONF_TAG,
- IPV4_DEVCONF_ARPFILTER,
- IPV4_DEVCONF_MEDIUM_ID,
- IPV4_DEVCONF_NOXFRM,
- IPV4_DEVCONF_NOPOLICY,
- IPV4_DEVCONF_FORCE_IGMP_VERSION,
- IPV4_DEVCONF_ARP_ANNOUNCE,
- IPV4_DEVCONF_ARP_IGNORE,
- IPV4_DEVCONF_PROMOTE_SECONDARIES,
- IPV4_DEVCONF_ARP_ACCEPT,
- IPV4_DEVCONF_ARP_NOTIFY,
- IPV4_DEVCONF_ACCEPT_LOCAL,
- IPV4_DEVCONF_SRC_VMARK,
- IPV4_DEVCONF_PROXY_ARP_PVLAN,
- IPV4_DEVCONF_ROUTE_LOCALNET,
- __IPV4_DEVCONF_MAX
-};
-
-#define IPV4_DEVCONF_MAX (__IPV4_DEVCONF_MAX - 1)
-
struct ipv4_devconf {
void *sysctl;
int data[IPV4_DEVCONF_MAX];
#define IP6SKB_FORWARDED 2
#define IP6SKB_REROUTED 4
#define IP6SKB_ROUTERALERT 8
+#define IP6SKB_FRAGMENTED 16
};
#define IP6CB(skb) ((struct inet6_skb_parm*)((skb)->cb))
--- /dev/null
+#ifndef __LINUX_LOCKREF_H
+#define __LINUX_LOCKREF_H
+
+/*
+ * Locked reference counts.
+ *
+ * These are different from just plain atomic refcounts in that they
+ * are atomic with respect to the spinlock that goes with them. In
+ * particular, there can be implementations that don't actually get
+ * the spinlock for the common decrement/increment operations, but they
+ * still have to check that the operation is done semantically as if
+ * the spinlock had been taken (using a cmpxchg operation that covers
+ * both the lock and the count word, or using memory transactions, for
+ * example).
+ */
+
+#include <linux/spinlock.h>
+
+struct lockref {
+ union {
+#ifdef CONFIG_CMPXCHG_LOCKREF
+ aligned_u64 lock_count;
+#endif
+ struct {
+ spinlock_t lock;
+ unsigned int count;
+ };
+ };
+};
+
+extern void lockref_get(struct lockref *);
+extern int lockref_get_not_zero(struct lockref *);
+extern int lockref_get_or_lock(struct lockref *);
+extern int lockref_put_or_lock(struct lockref *);
+
+#endif /* __LINUX_LOCKREF_H */
PALMAS_REG_SMPS7,
PALMAS_REG_SMPS8,
PALMAS_REG_SMPS9,
- PALMAS_REG_SMPS10,
+ PALMAS_REG_SMPS10_OUT2,
+ PALMAS_REG_SMPS10_OUT1,
/* LDO regulators */
PALMAS_REG_LDO1,
PALMAS_REG_LDO2,
int smps123;
int smps457;
- int range[PALMAS_REG_SMPS10];
- unsigned int ramp_delay[PALMAS_REG_SMPS10];
- unsigned int current_reg_mode[PALMAS_REG_SMPS10];
+ int range[PALMAS_REG_SMPS10_OUT1];
+ unsigned int ramp_delay[PALMAS_REG_SMPS10_OUT1];
+ unsigned int current_reg_mode[PALMAS_REG_SMPS10_OUT1];
};
struct palmas_resource {
#define S2MPS11_BUCK_N_VOLTAGES (S2MPS11_BUCK_VSEL_MASK + 1)
#define S2MPS11_RAMP_DELAY 25000 /* uV/us */
+
+#define S2MPS11_BUCK2_RAMP_SHIFT 6
+#define S2MPS11_BUCK34_RAMP_SHIFT 4
+#define S2MPS11_BUCK5_RAMP_SHIFT 6
+#define S2MPS11_BUCK16_RAMP_SHIFT 4
+#define S2MPS11_BUCK7810_RAMP_SHIFT 2
+#define S2MPS11_BUCK9_RAMP_SHIFT 0
+#define S2MPS11_BUCK2_RAMP_EN_SHIFT 3
+#define S2MPS11_BUCK3_RAMP_EN_SHIFT 2
+#define S2MPS11_BUCK4_RAMP_EN_SHIFT 1
+#define S2MPS11_BUCK6_RAMP_EN_SHIFT 0
#define S2MPS11_PMIC_EN_SHIFT 6
#define S2MPS11_REGULATOR_MAX (S2MPS11_REG_MAX - 3)
struct tps65217_bl_pdata *bl_pdata;
};
-/**
- * struct tps_info - packages regulator constraints
- * @name: Voltage regulator name
- * @min_uV: minimum micro volts
- * @max_uV: minimum micro volts
- * @vsel_to_uv: Function pointer to get voltage from selector
- * @uv_to_vsel: Function pointer to get selector from voltage
- *
- * This data is used to check the regualtor voltage limits while setting.
- */
-struct tps_info {
- const char *name;
- int min_uV;
- int max_uV;
- int (*vsel_to_uv)(unsigned int vsel);
- int (*uv_to_vsel)(int uV, unsigned int *vsel);
-};
-
/**
* struct tps65217 - tps65217 sub-driver chip access routines
*
unsigned int id;
struct regulator_desc desc[TPS65217_NUM_REGULATOR];
struct regulator_dev *rdev[TPS65217_NUM_REGULATOR];
- struct tps_info *info[TPS65217_NUM_REGULATOR];
struct regmap *regmap;
};
unsigned long pgoff, unsigned long flags);
#endif
unsigned long mmap_base; /* base of mmap area */
+ unsigned long mmap_legacy_base; /* base of mmap area in bottom-up allocations */
unsigned long task_size; /* size of task vm space */
unsigned long highest_vm_end; /* highest vma end address */
pgd_t * pgd;
* A structure to contain pointers to all per-process
* namespaces - fs (mount), uts, network, sysvipc, etc.
*
+ * The pid namespace is an exception -- it's accessed using
+ * task_active_pid_ns. The pid namespace here is the
+ * namespace that children will use.
+ *
* 'count' is the number of tasks holding a reference.
* The count for each namespace, then, will be the number
* of nsproxies pointing to it, not the number of tasks.
struct uts_namespace *uts_ns;
struct ipc_namespace *ipc_ns;
struct mnt_namespace *mnt_ns;
- struct pid_namespace *pid_ns;
+ struct pid_namespace *pid_ns_for_children;
struct net *net_ns;
};
extern struct nsproxy init_nsproxy;
--- /dev/null
+#ifndef __LINUX_PLATFORM_DATA_EFM32_SPI_H__
+#define __LINUX_PLATFORM_DATA_EFM32_SPI_H__
+
+#include <linux/types.h>
+
+/**
+ * struct efm32_spi_pdata
+ * @location: pinmux location for the I/O pins (to be written to the ROUTE
+ * register)
+ */
+struct efm32_spi_pdata {
+ u8 location;
+};
+#endif /* ifndef __LINUX_PLATFORM_DATA_EFM32_SPI_H__ */
#include <linux/list.h>
#include <linux/rbtree.h>
#include <linux/err.h>
+#include <linux/bug.h>
struct module;
struct device;
* @ack_base: Base ack address. If zero then the chip is clear on read.
* @wake_base: Base address for wake enables. If zero unsupported.
* @irq_reg_stride: Stride to use for chips where registers are not contiguous.
+ * @init_ack_masked: Ack all masked interrupts once during initalization.
+ * @mask_invert: Inverted mask register: cleared bits are masked out.
+ * @wake_invert: Inverted wake register: cleared bits are wake enabled.
* @runtime_pm: Hold a runtime PM lock on the device when accessing it.
*
* @num_regs: Number of registers in each control bank.
unsigned int ack_base;
unsigned int wake_base;
unsigned int irq_reg_stride;
- unsigned int mask_invert;
- unsigned int wake_invert;
- bool runtime_pm;
+ bool init_ack_masked:1;
+ bool mask_invert:1;
+ bool wake_invert:1;
+ bool runtime_pm:1;
int num_regs;
const char *id);
struct regulator *__must_check regulator_get_exclusive(struct device *dev,
const char *id);
+struct regulator *__must_check devm_regulator_get_exclusive(struct device *dev,
+ const char *id);
+struct regulator *__must_check regulator_get_optional(struct device *dev,
+ const char *id);
+struct regulator *__must_check devm_regulator_get_optional(struct device *dev,
+ const char *id);
void regulator_put(struct regulator *regulator);
void devm_regulator_put(struct regulator *regulator);
return NULL;
}
+static inline struct regulator *__must_check
+regulator_get_exclusive(struct device *dev, const char *id)
+{
+ return NULL;
+}
+
+static inline struct regulator *__must_check
+regulator_get_optional(struct device *dev, const char *id)
+{
+ return NULL;
+}
+
+
+static inline struct regulator *__must_check
+devm_regulator_get_optional(struct device *dev, const char *id)
+{
+ return NULL;
+}
+
static inline void regulator_put(struct regulator *regulator)
{
}
static inline int regulator_set_voltage_tol(struct regulator *regulator,
int new_uV, int tol_uV)
{
- return regulator_set_voltage(regulator,
- new_uV - tol_uV, new_uV + tol_uV);
+ if (regulator_set_voltage(regulator, new_uV, new_uV + tol_uV) == 0)
+ return 0;
+ else
+ return regulator_set_voltage(regulator,
+ new_uV - tol_uV, new_uV + tol_uV);
}
static inline int regulator_is_supported_voltage_tol(struct regulator *regulator,
REGULATOR_STATUS_UNDEFINED,
};
+/**
+ * Specify a range of voltages for regulator_map_linar_range() and
+ * regulator_list_linear_range().
+ *
+ * @min_uV: Lowest voltage in range
+ * @max_uV: Highest voltage in range
+ * @min_sel: Lowest selector for range
+ * @max_sel: Highest selector for range
+ * @uV_step: Step size
+ */
+struct regulator_linear_range {
+ unsigned int min_uV;
+ unsigned int max_uV;
+ unsigned int min_sel;
+ unsigned int max_sel;
+ unsigned int uV_step;
+};
+
/**
* struct regulator_ops - regulator operations.
*
unsigned int linear_min_sel;
unsigned int ramp_delay;
+ const struct regulator_linear_range *linear_ranges;
+ int n_linear_ranges;
+
const unsigned int *volt_table;
unsigned int vsel_reg;
int regulator_list_voltage_linear(struct regulator_dev *rdev,
unsigned int selector);
+int regulator_list_voltage_linear_range(struct regulator_dev *rdev,
+ unsigned int selector);
int regulator_list_voltage_table(struct regulator_dev *rdev,
unsigned int selector);
int regulator_map_voltage_linear(struct regulator_dev *rdev,
int min_uV, int max_uV);
+int regulator_map_voltage_linear_range(struct regulator_dev *rdev,
+ int min_uV, int max_uV);
int regulator_map_voltage_iterate(struct regulator_dev *rdev,
int min_uV, int max_uV);
int regulator_map_voltage_ascend(struct regulator_dev *rdev,
*/
#ifndef __FAN53555_H__
+#define __FAN53555_H__
/* VSEL ID */
enum {
unsigned always_on:1; /* regulator never off when system is on */
unsigned boot_on:1; /* bootloader/firmware enabled regulator */
unsigned apply_uV:1; /* apply uV constraint if min == max */
+ unsigned ramp_disable:1; /* disable ramp delay */
};
/**
*/
struct max8660_subdev_data {
int id;
- char *name;
+ const char *name;
struct regulator_init_data *platform_data;
};
--- /dev/null
+/*
+ * Copyright (C) 2011-2013 Freescale Semiconductor, Inc. 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#ifndef __LINUX_REG_PFUZE100_H
+#define __LINUX_REG_PFUZE100_H
+
+#define PFUZE100_SW1AB 0
+#define PFUZE100_SW1C 1
+#define PFUZE100_SW2 2
+#define PFUZE100_SW3A 3
+#define PFUZE100_SW3B 4
+#define PFUZE100_SW4 5
+#define PFUZE100_SWBST 6
+#define PFUZE100_VSNVS 7
+#define PFUZE100_VREFDDR 8
+#define PFUZE100_VGEN1 9
+#define PFUZE100_VGEN2 10
+#define PFUZE100_VGEN3 11
+#define PFUZE100_VGEN4 12
+#define PFUZE100_VGEN5 13
+#define PFUZE100_VGEN6 14
+#define PFUZE100_MAX_REGULATOR 15
+
+struct regulator_init_data;
+
+struct pfuze_regulator_platform_data {
+ struct regulator_init_data *init_data[PFUZE100_MAX_REGULATOR];
+};
+
+#endif /* __LINUX_REG_PFUZE100_H */
struct user_namespace;
#ifdef CONFIG_MMU
-extern unsigned long mmap_legacy_base(void);
extern void arch_pick_mmap_layout(struct mm_struct *mm);
extern unsigned long
arch_get_unmapped_area(struct file *, unsigned long, unsigned long,
struct spi_master *master;
u32 max_speed_hz;
u8 chip_select;
- u8 mode;
+ u16 mode;
#define SPI_CPHA 0x01 /* clock phase */
#define SPI_CPOL 0x02 /* clock polarity */
#define SPI_MODE_0 (0|0) /* (original MicroWire) */
#define SPI_LOOP 0x20 /* loopback mode */
#define SPI_NO_CS 0x40 /* 1 dev/bus, no chipselect */
#define SPI_READY 0x80 /* slave pulls low to pause */
+#define SPI_TX_DUAL 0x100 /* transmit with 2 wires */
+#define SPI_TX_QUAD 0x200 /* transmit with 4 wires */
+#define SPI_RX_DUAL 0x400 /* receive with 2 wires */
+#define SPI_RX_QUAD 0x800 /* receive with 4 wires */
u8 bits_per_word;
int irq;
void *controller_state;
* suported. If set, the SPI core will reject any transfer with an
* unsupported bits_per_word. If not set, this value is simply ignored,
* and it's up to the individual driver to perform any validation.
+ * @min_speed_hz: Lowest supported transfer speed
+ * @max_speed_hz: Highest supported transfer speed
* @flags: other constraints relevant to this driver
* @bus_lock_spinlock: spinlock for SPI bus locking
* @bus_lock_mutex: mutex for SPI bus locking
* @busy: message pump is busy
* @running: message pump is running
* @rt: whether this queue is set to run as a realtime task
+ * @auto_runtime_pm: the core should ensure a runtime PM reference is held
+ * while the hardware is prepared, using the parent
+ * device for the spidev
* @prepare_transfer_hardware: a message will soon arrive from the queue
* so the subsystem requests the driver to prepare the transfer hardware
* by issuing this call
/* bitmask of supported bits_per_word for transfers */
u32 bits_per_word_mask;
#define SPI_BPW_MASK(bits) BIT((bits) - 1)
-#define SPI_BIT_MASK(bits) (((bits) == 32) ? ~0UL : (BIT(bits) - 1))
+#define SPI_BIT_MASK(bits) (((bits) == 32) ? ~0U : (BIT(bits) - 1))
#define SPI_BPW_RANGE_MASK(min, max) (SPI_BIT_MASK(max) - SPI_BIT_MASK(min - 1))
+ /* limits on transfer speed */
+ u32 min_speed_hz;
+ u32 max_speed_hz;
+
/* other constraints relevant to this driver */
u16 flags;
#define SPI_MASTER_HALF_DUPLEX BIT(0) /* can't do full duplex */
bool busy;
bool running;
bool rt;
+ bool auto_runtime_pm;
int (*prepare_transfer_hardware)(struct spi_master *master);
int (*transfer_one_message)(struct spi_master *master,
struct spi_message *mesg);
int (*unprepare_transfer_hardware)(struct spi_master *master);
+
/* gpio chip select */
int *cs_gpios;
};
* @rx_buf: data to be read (dma-safe memory), or NULL
* @tx_dma: DMA address of tx_buf, if @spi_message.is_dma_mapped
* @rx_dma: DMA address of rx_buf, if @spi_message.is_dma_mapped
+ * @tx_nbits: number of bits used for writting. If 0 the default
+ * (SPI_NBITS_SINGLE) is used.
+ * @rx_nbits: number of bits used for reading. If 0 the default
+ * (SPI_NBITS_SINGLE) is used.
* @len: size of rx and tx buffers (in bytes)
* @speed_hz: Select a speed other than the device default for this
* transfer. If 0 the default (from @spi_device) is used.
* by the results of previous messages and where the whole transaction
* ends when the chipselect goes intactive.
*
+ * When SPI can transfer in 1x,2x or 4x. It can get this tranfer information
+ * from device through @tx_nbits and @rx_nbits. In Bi-direction, these
+ * two should both be set. User can set transfer mode with SPI_NBITS_SINGLE(1x)
+ * SPI_NBITS_DUAL(2x) and SPI_NBITS_QUAD(4x) to support these three transfer.
+ *
* The code that submits an spi_message (and its spi_transfers)
* to the lower layers is responsible for managing its memory.
* Zero-initialize every field you don't set up explicitly, to
dma_addr_t rx_dma;
unsigned cs_change:1;
+ u8 tx_nbits;
+ u8 rx_nbits;
+#define SPI_NBITS_SINGLE 0x01 /* 1bit transfer */
+#define SPI_NBITS_DUAL 0x02 /* 2bits transfer */
+#define SPI_NBITS_QUAD 0x04 /* 4bits transfer */
u8 bits_per_word;
u16 delay_usecs;
u32 speed_hz;
/* completion is reported through a callback */
void (*complete)(void *context);
void *context;
+ unsigned frame_length;
unsigned actual_length;
int status;
/* mode becomes spi_device.mode, and is essential for chips
* where the default of SPI_CS_HIGH = 0 is wrong.
*/
- u8 mode;
+ u16 mode;
/* ... may need additional spi_device chip config data here.
* avoid stuff protocol drivers can set; but include stuff
#include <linux/workqueue.h>
struct spi_bitbang {
- struct workqueue_struct *workqueue;
- struct work_struct work;
-
spinlock_t lock;
- struct list_head queue;
u8 busy;
u8 use_dma;
u8 flags; /* extra spi->mode support */
*/
extern int spi_bitbang_setup(struct spi_device *spi);
extern void spi_bitbang_cleanup(struct spi_device *spi);
-extern int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m);
extern int spi_bitbang_setup_transfer(struct spi_device *spi,
struct spi_transfer *t);
__ret; \
})
+#define __wait_event_interruptible_lock_irq_timeout(wq, condition, \
+ lock, ret) \
+do { \
+ DEFINE_WAIT(__wait); \
+ \
+ for (;;) { \
+ prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); \
+ if (condition) \
+ break; \
+ if (signal_pending(current)) { \
+ ret = -ERESTARTSYS; \
+ break; \
+ } \
+ spin_unlock_irq(&lock); \
+ ret = schedule_timeout(ret); \
+ spin_lock_irq(&lock); \
+ if (!ret) \
+ break; \
+ } \
+ finish_wait(&wq, &__wait); \
+} while (0)
+
+/**
+ * wait_event_interruptible_lock_irq_timeout - sleep until a condition gets true or a timeout elapses.
+ * The condition is checked under the lock. This is expected
+ * to be called with the lock taken.
+ * @wq: the waitqueue to wait on
+ * @condition: a C expression for the event to wait for
+ * @lock: a locked spinlock_t, which will be released before schedule()
+ * and reacquired afterwards.
+ * @timeout: timeout, in jiffies
+ *
+ * The process is put to sleep (TASK_INTERRUPTIBLE) until the
+ * @condition evaluates to true or signal is received. The @condition is
+ * checked each time the waitqueue @wq is woken up.
+ *
+ * wake_up() has to be called after changing any variable that could
+ * change the result of the wait condition.
+ *
+ * This is supposed to be called while holding the lock. The lock is
+ * dropped before going to sleep and is reacquired afterwards.
+ *
+ * The function returns 0 if the @timeout elapsed, -ERESTARTSYS if it
+ * was interrupted by a signal, and the remaining jiffies otherwise
+ * if the condition evaluated to true before the timeout elapsed.
+ */
+#define wait_event_interruptible_lock_irq_timeout(wq, condition, lock, \
+ timeout) \
+({ \
+ int __ret = timeout; \
+ \
+ if (!(condition)) \
+ __wait_event_interruptible_lock_irq_timeout( \
+ wq, condition, lock, __ret); \
+ __ret; \
+})
+
/*
* These are the old interfaces to sleep waiting for an event.
/* local bh are disabled so it is ok to use _BH */
NET_ADD_STATS_BH(sock_net(sk),
LINUX_MIB_BUSYPOLLRXPACKETS, rc);
+ cpu_relax();
} while (!nonblock && skb_queue_empty(&sk->sk_receive_queue) &&
!need_resched() && !busy_loop_timeout(end_time));
struct list_head ops_list; /* private */
struct list_head family_list; /* private */
struct list_head mcast_groups; /* private */
+ struct module *module;
};
/**
struct list_head ops_list;
};
-extern int genl_register_family(struct genl_family *family);
-extern int genl_register_family_with_ops(struct genl_family *family,
+extern int __genl_register_family(struct genl_family *family);
+
+static inline int genl_register_family(struct genl_family *family)
+{
+ family->module = THIS_MODULE;
+ return __genl_register_family(family);
+}
+
+extern int __genl_register_family_with_ops(struct genl_family *family,
struct genl_ops *ops, size_t n_ops);
+
+static inline int genl_register_family_with_ops(struct genl_family *family,
+ struct genl_ops *ops, size_t n_ops)
+{
+ family->module = THIS_MODULE;
+ return __genl_register_family_with_ops(family, ops, n_ops);
+}
+
extern int genl_unregister_family(struct genl_family *family);
extern int genl_register_ops(struct genl_family *, struct genl_ops *ops);
extern int genl_unregister_ops(struct genl_family *, struct genl_ops *ops);
extern void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk,
__be32 mtu);
extern void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark);
+extern void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif,
+ u32 mark);
extern void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk);
struct netlink_callback;
IEEE80211_HW_SUPPORTS_RC_TABLE = 1<<24,
IEEE80211_HW_P2P_DEV_ADDR_FOR_INTF = 1<<25,
IEEE80211_HW_TIMING_BEACON_ONLY = 1<<26,
+ IEEE80211_HW_SUPPORTS_HT_CCK_RATES = 1<<27,
};
/**
return hoplimit;
}
+static inline int ip_skb_dst_mtu(struct sk_buff *skb)
+{
+ struct inet_sock *inet = skb->sk ? inet_sk(skb->sk) : NULL;
+
+ return (inet && inet->pmtudisc == IP_PMTUDISC_PROBE) ?
+ skb_dst(skb)->dev->mtu : dst_mtu(skb_dst(skb));
+}
+
#endif /* _ROUTE_H */
struct sk_buff *skb);
int (*transport_finish)(struct sk_buff *skb,
int async);
+ void (*local_error)(struct sk_buff *skb, u32 mtu);
};
extern int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo);
extern int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo);
+extern struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family);
+extern void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
extern void xfrm_state_delete_tunnel(struct xfrm_state *x);
extern int xfrm_output_resume(struct sk_buff *skb, int err);
extern int xfrm_output(struct sk_buff *skb);
extern int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb);
+extern void xfrm_local_error(struct sk_buff *skb, int mtu);
extern int xfrm4_extract_header(struct sk_buff *skb);
extern int xfrm4_extract_input(struct xfrm_state *x, struct sk_buff *skb);
extern int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi,
extern int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler, unsigned short family);
extern int xfrm4_mode_tunnel_input_register(struct xfrm_tunnel *handler);
extern int xfrm4_mode_tunnel_input_deregister(struct xfrm_tunnel *handler);
+extern void xfrm4_local_error(struct sk_buff *skb, u32 mtu);
extern int xfrm6_extract_header(struct sk_buff *skb);
extern int xfrm6_extract_input(struct xfrm_state *x, struct sk_buff *skb);
extern int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi);
extern int xfrm6_output_finish(struct sk_buff *skb);
extern int xfrm6_find_1stfragopt(struct xfrm_state *x, struct sk_buff *skb,
u8 **prevhdr);
+extern void xfrm6_local_error(struct sk_buff *skb, u32 mtu);
#ifdef CONFIG_XFRM
extern int xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb);
#define _UAPI_CM4000_H_
#include <linux/types.h>
+#include <linux/ioctl.h>
#define MAX_ATR 33
__u8 reserved;
};
+/* index values for the variables in ipv4_devconf */
+enum
+{
+ IPV4_DEVCONF_FORWARDING=1,
+ IPV4_DEVCONF_MC_FORWARDING,
+ IPV4_DEVCONF_PROXY_ARP,
+ IPV4_DEVCONF_ACCEPT_REDIRECTS,
+ IPV4_DEVCONF_SECURE_REDIRECTS,
+ IPV4_DEVCONF_SEND_REDIRECTS,
+ IPV4_DEVCONF_SHARED_MEDIA,
+ IPV4_DEVCONF_RP_FILTER,
+ IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE,
+ IPV4_DEVCONF_BOOTP_RELAY,
+ IPV4_DEVCONF_LOG_MARTIANS,
+ IPV4_DEVCONF_TAG,
+ IPV4_DEVCONF_ARPFILTER,
+ IPV4_DEVCONF_MEDIUM_ID,
+ IPV4_DEVCONF_NOXFRM,
+ IPV4_DEVCONF_NOPOLICY,
+ IPV4_DEVCONF_FORCE_IGMP_VERSION,
+ IPV4_DEVCONF_ARP_ANNOUNCE,
+ IPV4_DEVCONF_ARP_IGNORE,
+ IPV4_DEVCONF_PROMOTE_SECONDARIES,
+ IPV4_DEVCONF_ARP_ACCEPT,
+ IPV4_DEVCONF_ARP_NOTIFY,
+ IPV4_DEVCONF_ACCEPT_LOCAL,
+ IPV4_DEVCONF_SRC_VMARK,
+ IPV4_DEVCONF_PROXY_ARP_PVLAN,
+ IPV4_DEVCONF_ROUTE_LOCALNET,
+ __IPV4_DEVCONF_MAX
+};
+
+#define IPV4_DEVCONF_MAX (__IPV4_DEVCONF_MAX - 1)
+
#endif /* _UAPI_LINUX_IP_H */
Memory Resource Controller Swap Extension comes with its price in
a bigger memory consumption. General purpose distribution kernels
which want to enable the feature but keep it disabled by default
- and let the user enable it by swapaccount boot command line
+ and let the user enable it by swapaccount=1 boot command line
parameter should have this option unselected.
For those who want to have the feature enabled by default should
select this option (if, for some reason, they need to disable it
goto out_unlock1;
}
+ ipc_lock_object(&msq->q_perm);
+
for (;;) {
struct msg_sender s;
err = -EACCES;
if (ipcperms(ns, &msq->q_perm, S_IWUGO))
- goto out_unlock1;
+ goto out_unlock0;
err = security_msg_queue_msgsnd(msq, msg, msgflg);
if (err)
- goto out_unlock1;
+ goto out_unlock0;
if (msgsz + msq->q_cbytes <= msq->q_qbytes &&
1 + msq->q_qnum <= msq->q_qbytes) {
/* queue full, wait: */
if (msgflg & IPC_NOWAIT) {
err = -EAGAIN;
- goto out_unlock1;
+ goto out_unlock0;
}
- ipc_lock_object(&msq->q_perm);
ss_add(msq, &s);
if (!ipc_rcu_getref(msq)) {
goto out_unlock0;
}
- ipc_unlock_object(&msq->q_perm);
}
-
- ipc_lock_object(&msq->q_perm);
msq->q_lspid = task_tgid_vnr(current);
msq->q_stime = get_seconds();
static struct msg_msg *find_msg(struct msg_queue *msq, long *msgtyp, int mode)
{
- struct msg_msg *msg;
+ struct msg_msg *msg, *found = NULL;
long count = 0;
list_for_each_entry(msg, &msq->q_messages, m_list) {
*msgtyp, mode)) {
if (mode == SEARCH_LESSEQUAL && msg->m_type != 1) {
*msgtyp = msg->m_type - 1;
+ found = msg;
} else if (mode == SEARCH_NUMBER) {
if (*msgtyp == count)
return msg;
}
}
- return ERR_PTR(-EAGAIN);
+ return found ?: ERR_PTR(-EAGAIN);
}
long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgflg,
struct dentry *d = cgrp->dentry;
struct cgroup_event *event, *tmp;
struct cgroup_subsys *ss;
+ struct cgroup *child;
bool empty;
lockdep_assert_held(&d->d_inode->i_mutex);
* @cgrp from being removed while __put_css_set() is in progress.
*/
read_lock(&css_set_lock);
- empty = list_empty(&cgrp->cset_links) && list_empty(&cgrp->children);
+ empty = list_empty(&cgrp->cset_links);
read_unlock(&css_set_lock);
if (!empty)
return -EBUSY;
+ /*
+ * Make sure there's no live children. We can't test ->children
+ * emptiness as dead children linger on it while being destroyed;
+ * otherwise, "rmdir parent/child parent" may fail with -EBUSY.
+ */
+ empty = true;
+ rcu_read_lock();
+ list_for_each_entry_rcu(child, &cgrp->children, sibling) {
+ empty = cgroup_is_dead(child);
+ if (!empty)
+ break;
+ }
+ rcu_read_unlock();
+ if (!empty)
+ return -EBUSY;
+
/*
* Block new css_tryget() by killing css refcnts. cgroup core
* guarantees that, by the time ->css_offline() is invoked, no new
/*
* Cpusets with tasks - existing or newly being attached - can't
- * have empty cpus_allowed or mems_allowed.
+ * be changed to have empty cpus_allowed or mems_allowed.
*/
ret = -ENOSPC;
- if ((cgroup_task_count(cur->css.cgroup) || cur->attach_in_progress) &&
- (cpumask_empty(trial->cpus_allowed) &&
- nodes_empty(trial->mems_allowed)))
- goto out;
+ if ((cgroup_task_count(cur->css.cgroup) || cur->attach_in_progress)) {
+ if (!cpumask_empty(cur->cpus_allowed) &&
+ cpumask_empty(trial->cpus_allowed))
+ goto out;
+ if (!nodes_empty(cur->mems_allowed) &&
+ nodes_empty(trial->mems_allowed))
+ goto out;
+ }
ret = 0;
out:
* don't allow the creation of threads.
*/
if ((clone_flags & (CLONE_VM|CLONE_NEWPID)) &&
- (task_active_pid_ns(current) != current->nsproxy->pid_ns))
+ (task_active_pid_ns(current) !=
+ current->nsproxy->pid_ns_for_children))
return ERR_PTR(-EINVAL);
retval = security_task_create(clone_flags);
if (pid != &init_struct_pid) {
retval = -ENOMEM;
- pid = alloc_pid(p->nsproxy->pid_ns);
+ pid = alloc_pid(p->nsproxy->pid_ns_for_children);
if (!pid)
goto bad_fork_cleanup_io;
}
static struct kmem_cache *nsproxy_cachep;
struct nsproxy init_nsproxy = {
- .count = ATOMIC_INIT(1),
- .uts_ns = &init_uts_ns,
+ .count = ATOMIC_INIT(1),
+ .uts_ns = &init_uts_ns,
#if defined(CONFIG_POSIX_MQUEUE) || defined(CONFIG_SYSVIPC)
- .ipc_ns = &init_ipc_ns,
+ .ipc_ns = &init_ipc_ns,
#endif
- .mnt_ns = NULL,
- .pid_ns = &init_pid_ns,
+ .mnt_ns = NULL,
+ .pid_ns_for_children = &init_pid_ns,
#ifdef CONFIG_NET
- .net_ns = &init_net,
+ .net_ns = &init_net,
#endif
};
goto out_ipc;
}
- new_nsp->pid_ns = copy_pid_ns(flags, user_ns, tsk->nsproxy->pid_ns);
- if (IS_ERR(new_nsp->pid_ns)) {
- err = PTR_ERR(new_nsp->pid_ns);
+ new_nsp->pid_ns_for_children =
+ copy_pid_ns(flags, user_ns, tsk->nsproxy->pid_ns_for_children);
+ if (IS_ERR(new_nsp->pid_ns_for_children)) {
+ err = PTR_ERR(new_nsp->pid_ns_for_children);
goto out_pid;
}
return new_nsp;
out_net:
- if (new_nsp->pid_ns)
- put_pid_ns(new_nsp->pid_ns);
+ if (new_nsp->pid_ns_for_children)
+ put_pid_ns(new_nsp->pid_ns_for_children);
out_pid:
if (new_nsp->ipc_ns)
put_ipc_ns(new_nsp->ipc_ns);
put_uts_ns(ns->uts_ns);
if (ns->ipc_ns)
put_ipc_ns(ns->ipc_ns);
- if (ns->pid_ns)
- put_pid_ns(ns->pid_ns);
+ if (ns->pid_ns_for_children)
+ put_pid_ns(ns->pid_ns_for_children);
put_net(ns->net_ns);
kmem_cache_free(nsproxy_cachep, ns);
}
if (ancestor != active)
return -EINVAL;
- put_pid_ns(nsproxy->pid_ns);
- nsproxy->pid_ns = get_pid_ns(new);
+ put_pid_ns(nsproxy->pid_ns_for_children);
+ nsproxy->pid_ns_for_children = get_pid_ns(new);
return 0;
}
BUG_ON(bits > 32);
WARN_ON(!irqs_disabled());
read_sched_clock = read;
- sched_clock_mask = (1 << bits) - 1;
+ sched_clock_mask = (1ULL << bits) - 1;
cd.rate = rate;
/* calculate the mult/shift to convert counter ticks to ns. */
* Don't allow the user to think they can get
* full NO_HZ with this machine.
*/
- WARN_ONCE(1, "NO_HZ FULL will not work with unstable sched clock");
+ WARN_ONCE(have_nohz_full_mask,
+ "NO_HZ FULL will not work with unstable sched clock");
return false;
}
#endif
void __init tick_nohz_init(void)
{
- int cpu;
-
if (!have_nohz_full_mask) {
if (tick_nohz_init_all() < 0)
return;
static int timer_list_show(struct seq_file *m, void *v)
{
struct timer_list_iter *iter = v;
- u64 now = ktime_to_ns(ktime_get());
if (iter->cpu == -1 && !iter->second_pass)
- timer_list_header(m, now);
+ timer_list_header(m, iter->now);
else if (!iter->second_pass)
print_cpu(m, iter->cpu, iter->now);
#ifdef CONFIG_GENERIC_CLOCKEVENTS
return;
}
-static void *timer_list_start(struct seq_file *file, loff_t *offset)
+static void *move_iter(struct timer_list_iter *iter, loff_t offset)
{
- struct timer_list_iter *iter = file->private;
-
- if (!*offset) {
- iter->cpu = -1;
- iter->now = ktime_to_ns(ktime_get());
- } else if (iter->cpu >= nr_cpu_ids) {
+ for (; offset; offset--) {
+ iter->cpu = cpumask_next(iter->cpu, cpu_online_mask);
+ if (iter->cpu >= nr_cpu_ids) {
#ifdef CONFIG_GENERIC_CLOCKEVENTS
- if (!iter->second_pass) {
- iter->cpu = -1;
- iter->second_pass = true;
- } else
- return NULL;
+ if (!iter->second_pass) {
+ iter->cpu = -1;
+ iter->second_pass = true;
+ } else
+ return NULL;
#else
- return NULL;
+ return NULL;
#endif
+ }
}
return iter;
}
+static void *timer_list_start(struct seq_file *file, loff_t *offset)
+{
+ struct timer_list_iter *iter = file->private;
+
+ if (!*offset)
+ iter->now = ktime_to_ns(ktime_get());
+ iter->cpu = -1;
+ iter->second_pass = false;
+ return move_iter(iter, *offset);
+}
+
static void *timer_list_next(struct seq_file *file, void *v, loff_t *offset)
{
struct timer_list_iter *iter = file->private;
- iter->cpu = cpumask_next(iter->cpu, cpu_online_mask);
++*offset;
- return timer_list_start(file, offset);
+ return move_iter(iter, 1);
}
static void timer_list_stop(struct seq_file *seq, void *v)
/**
* wake_up_atomic_t - Wake up a waiter on a atomic_t
- * @word: The word being waited on, a kernel virtual address
- * @bit: The bit of the word being waited on
+ * @p: The atomic_t being waited on, a kernel virtual address
*
* Wake up anyone waiting for the atomic_t to go to zero.
*
dump_stack();
}
+ /*
+ * The following prevents a kworker from hogging CPU on !PREEMPT
+ * kernels, where a requeueing work item waiting for something to
+ * happen could deadlock with stop_machine as such work item could
+ * indefinitely requeue itself while all other CPUs are trapped in
+ * stop_machine.
+ */
+ cond_resched();
+
spin_lock_irq(&pool->lock);
/* clear cpu intensive status */
config PERCPU_RWSEM
boolean
+config ARCH_USE_CMPXCHG_LOCKREF
+ bool
+
+config CMPXCHG_LOCKREF
+ def_bool y if ARCH_USE_CMPXCHG_LOCKREF
+ depends on SMP
+ depends on !GENERIC_LOCKBREAK
+ depends on !DEBUG_SPINLOCK
+ depends on !DEBUG_LOCK_ALLOC
+
config CRC_CCITT
tristate "CRC-CCITT functions"
help
lib-$(CONFIG_SMP) += cpumask.o
lib-y += kobject.o klist.o
+obj-y += lockref.o
obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \
bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \
--- /dev/null
+#include <linux/export.h>
+#include <linux/lockref.h>
+
+#ifdef CONFIG_CMPXCHG_LOCKREF
+
+/*
+ * Note that the "cmpxchg()" reloads the "old" value for the
+ * failure case.
+ */
+#define CMPXCHG_LOOP(CODE, SUCCESS) do { \
+ struct lockref old; \
+ BUILD_BUG_ON(sizeof(old) != 8); \
+ old.lock_count = ACCESS_ONCE(lockref->lock_count); \
+ while (likely(arch_spin_value_unlocked(old.lock.rlock.raw_lock))) { \
+ struct lockref new = old, prev = old; \
+ CODE \
+ old.lock_count = cmpxchg(&lockref->lock_count, \
+ old.lock_count, new.lock_count); \
+ if (likely(old.lock_count == prev.lock_count)) { \
+ SUCCESS; \
+ } \
+ } \
+} while (0)
+
+#else
+
+#define CMPXCHG_LOOP(CODE, SUCCESS) do { } while (0)
+
+#endif
+
+/**
+ * lockref_get - Increments reference count unconditionally
+ * @lockcnt: pointer to lockref structure
+ *
+ * This operation is only valid if you already hold a reference
+ * to the object, so you know the count cannot be zero.
+ */
+void lockref_get(struct lockref *lockref)
+{
+ CMPXCHG_LOOP(
+ new.count++;
+ ,
+ return;
+ );
+
+ spin_lock(&lockref->lock);
+ lockref->count++;
+ spin_unlock(&lockref->lock);
+}
+EXPORT_SYMBOL(lockref_get);
+
+/**
+ * lockref_get_not_zero - Increments count unless the count is 0
+ * @lockcnt: pointer to lockref structure
+ * Return: 1 if count updated successfully or 0 if count was zero
+ */
+int lockref_get_not_zero(struct lockref *lockref)
+{
+ int retval;
+
+ CMPXCHG_LOOP(
+ new.count++;
+ if (!old.count)
+ return 0;
+ ,
+ return 1;
+ );
+
+ spin_lock(&lockref->lock);
+ retval = 0;
+ if (lockref->count) {
+ lockref->count++;
+ retval = 1;
+ }
+ spin_unlock(&lockref->lock);
+ return retval;
+}
+EXPORT_SYMBOL(lockref_get_not_zero);
+
+/**
+ * lockref_get_or_lock - Increments count unless the count is 0
+ * @lockcnt: pointer to lockref structure
+ * Return: 1 if count updated successfully or 0 if count was zero
+ * and we got the lock instead.
+ */
+int lockref_get_or_lock(struct lockref *lockref)
+{
+ CMPXCHG_LOOP(
+ new.count++;
+ if (!old.count)
+ break;
+ ,
+ return 1;
+ );
+
+ spin_lock(&lockref->lock);
+ if (!lockref->count)
+ return 0;
+ lockref->count++;
+ spin_unlock(&lockref->lock);
+ return 1;
+}
+EXPORT_SYMBOL(lockref_get_or_lock);
+
+/**
+ * lockref_put_or_lock - decrements count unless count <= 1 before decrement
+ * @lockcnt: pointer to lockref structure
+ * Return: 1 if count updated successfully or 0 if count <= 1 and lock taken
+ */
+int lockref_put_or_lock(struct lockref *lockref)
+{
+ CMPXCHG_LOOP(
+ new.count--;
+ if (old.count <= 1)
+ break;
+ ,
+ return 1;
+ );
+
+ spin_lock(&lockref->lock);
+ if (lockref->count <= 1)
+ return 0;
+ lockref->count--;
+ spin_unlock(&lockref->lock);
+ return 1;
+}
+EXPORT_SYMBOL(lockref_put_or_lock);
exit:
return ret;
}
-EXPORT_SYMBOL_GPL(lz4_compress);
+EXPORT_SYMBOL(lz4_compress);
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("LZ4 compressor");
return ret;
}
#ifndef STATIC
-EXPORT_SYMBOL_GPL(lz4_decompress);
+EXPORT_SYMBOL(lz4_decompress);
#endif
int lz4_decompress_unknownoutputsize(const char *src, size_t src_len,
return ret;
}
#ifndef STATIC
-EXPORT_SYMBOL_GPL(lz4_decompress_unknownoutputsize);
+EXPORT_SYMBOL(lz4_decompress_unknownoutputsize);
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("LZ4 Decompressor");
#endif
exit:
return ret;
}
-EXPORT_SYMBOL_GPL(lz4hc_compress);
+EXPORT_SYMBOL(lz4hc_compress);
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("LZ4HC compressor");
#ifdef CONFIG_MEMCG_SWAP
static int __init enable_swap_account(char *s)
{
- /* consider enabled if no parameter or 1 is given */
if (!strcmp(s, "1"))
really_do_swap_account = 1;
else if (!strcmp(s, "0"))
#include <linux/swap.h>
#include <linux/capability.h>
#include <linux/fs.h>
+#include <linux/swapops.h>
#include <linux/highmem.h>
#include <linux/security.h>
#include <linux/syscalls.h>
return pmd;
}
+static pte_t move_soft_dirty_pte(pte_t pte)
+{
+ /*
+ * Set soft dirty bit so we can notice
+ * in userspace the ptes were moved.
+ */
+#ifdef CONFIG_MEM_SOFT_DIRTY
+ if (pte_present(pte))
+ pte = pte_mksoft_dirty(pte);
+ else if (is_swap_pte(pte))
+ pte = pte_swp_mksoft_dirty(pte);
+ else if (pte_file(pte))
+ pte = pte_file_mksoft_dirty(pte);
+#endif
+ return pte;
+}
+
static void move_ptes(struct vm_area_struct *vma, pmd_t *old_pmd,
unsigned long old_addr, unsigned long old_end,
struct vm_area_struct *new_vma, pmd_t *new_pmd,
continue;
pte = ptep_get_and_clear(mm, old_addr, old_pte);
pte = move_pte(pte, new_vma->vm_page_prot, old_addr, new_addr);
- set_pte_at(mm, new_addr, new_pte, pte_mksoft_dirty(pte));
+ pte = move_soft_dirty_pte(pte);
+ set_pte_at(mm, new_addr, new_pte, pte);
}
arch_leave_lazy_mmu_mode();
vm_flags);
if (we_locked)
unlock_page(page);
-
- if (page_test_and_clear_young(page_to_pfn(page)))
- referenced++;
}
out:
return referenced;
/* common code */
-static char *shmem_dname(struct dentry *dentry, char *buffer, int buflen)
-{
- return dynamic_dname(dentry, buffer, buflen, "/%s (deleted)",
- dentry->d_name.name);
-}
-
static struct dentry_operations anon_ops = {
- .d_dname = shmem_dname
+ .d_dname = simple_dname
};
/**
static inline struct kmem_cache *cache_from_memcg(struct kmem_cache *s, int idx)
{
+ if (!s->memcg_params)
+ return NULL;
return s->memcg_params->memcg_caches[idx];
}
switch (packet_type) {
case BATADV_UNICAST:
- batadv_unicast_prepare_skb(skb, orig_node);
+ if (!batadv_unicast_prepare_skb(skb, orig_node))
+ goto out;
+
header_len = sizeof(struct batadv_unicast_packet);
break;
case BATADV_UNICAST_4ADDR:
- batadv_unicast_4addr_prepare_skb(bat_priv, skb, orig_node,
- packet_subtype);
+ if (!batadv_unicast_4addr_prepare_skb(bat_priv, skb, orig_node,
+ packet_subtype))
+ goto out;
+
header_len = sizeof(struct batadv_unicast_4addr_packet);
break;
default:
mdst = br_mdb_get(br, skb, vid);
if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) &&
- br_multicast_querier_exists(br))
+ br_multicast_querier_exists(br, eth_hdr(skb)))
br_multicast_deliver(mdst, skb);
else
br_flood_deliver(br, skb, false);
if (!pv)
return;
- for_each_set_bit_from(vid, pv->vlan_bitmap, BR_VLAN_BITMAP_LEN) {
+ for_each_set_bit_from(vid, pv->vlan_bitmap, VLAN_N_VID) {
f = __br_fdb_get(br, br->dev->dev_addr, vid);
if (f && f->is_local && !f->dst)
fdb_delete(br, f);
/* VID was specified, so use it. */
err = __br_fdb_add(ndm, p, addr, nlh_flags, vid);
} else {
- if (!pv || bitmap_empty(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN)) {
+ if (!pv || bitmap_empty(pv->vlan_bitmap, VLAN_N_VID)) {
err = __br_fdb_add(ndm, p, addr, nlh_flags, 0);
goto out;
}
* specify a VLAN. To be nice, add/update entry for every
* vlan on this port.
*/
- for_each_set_bit(vid, pv->vlan_bitmap, BR_VLAN_BITMAP_LEN) {
+ for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) {
err = __br_fdb_add(ndm, p, addr, nlh_flags, vid);
if (err)
goto out;
err = __br_fdb_delete(p, addr, vid);
} else {
- if (!pv || bitmap_empty(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN)) {
+ if (!pv || bitmap_empty(pv->vlan_bitmap, VLAN_N_VID)) {
err = __br_fdb_delete(p, addr, 0);
goto out;
}
* vlan on this port.
*/
err = -ENOENT;
- for_each_set_bit(vid, pv->vlan_bitmap, BR_VLAN_BITMAP_LEN) {
+ for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) {
err &= __br_fdb_delete(p, addr, vid);
}
}
} else if (is_multicast_ether_addr(dest)) {
mdst = br_mdb_get(br, skb, vid);
if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) &&
- br_multicast_querier_exists(br)) {
+ br_multicast_querier_exists(br, eth_hdr(skb))) {
if ((mdst && mdst->mglist) ||
br_multicast_is_router(br))
skb2 = skb;
if (!netif_running(br->dev) || br->multicast_disabled)
return -EINVAL;
- if (timer_pending(&br->multicast_querier_timer))
- return -EBUSY;
-
ip.proto = entry->addr.proto;
- if (ip.proto == htons(ETH_P_IP))
+ if (ip.proto == htons(ETH_P_IP)) {
+ if (timer_pending(&br->ip4_querier.timer))
+ return -EBUSY;
+
ip.u.ip4 = entry->addr.u.ip4;
#if IS_ENABLED(CONFIG_IPV6)
- else
+ } else {
+ if (timer_pending(&br->ip6_querier.timer))
+ return -EBUSY;
+
ip.u.ip6 = entry->addr.u.ip6;
#endif
+ }
spin_lock_bh(&br->multicast_lock);
mdb = mlock_dereference(br->mdb, br);
#include "br_private.h"
-static void br_multicast_start_querier(struct net_bridge *br);
+static void br_multicast_start_querier(struct net_bridge *br,
+ struct bridge_mcast_query *query);
unsigned int br_mdb_rehash_seq;
static inline int br_ip_equal(const struct br_ip *a, const struct br_ip *b)
{
}
-static void br_multicast_querier_expired(unsigned long data)
+static void br_multicast_querier_expired(struct net_bridge *br,
+ struct bridge_mcast_query *query)
{
- struct net_bridge *br = (void *)data;
-
spin_lock(&br->multicast_lock);
if (!netif_running(br->dev) || br->multicast_disabled)
goto out;
- br_multicast_start_querier(br);
+ br_multicast_start_querier(br, query);
out:
spin_unlock(&br->multicast_lock);
}
+static void br_ip4_multicast_querier_expired(unsigned long data)
+{
+ struct net_bridge *br = (void *)data;
+
+ br_multicast_querier_expired(br, &br->ip4_query);
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+static void br_ip6_multicast_querier_expired(unsigned long data)
+{
+ struct net_bridge *br = (void *)data;
+
+ br_multicast_querier_expired(br, &br->ip6_query);
+}
+#endif
+
static void __br_multicast_send_query(struct net_bridge *br,
struct net_bridge_port *port,
struct br_ip *ip)
}
static void br_multicast_send_query(struct net_bridge *br,
- struct net_bridge_port *port, u32 sent)
+ struct net_bridge_port *port,
+ struct bridge_mcast_query *query)
{
unsigned long time;
struct br_ip br_group;
+ struct bridge_mcast_querier *querier = NULL;
if (!netif_running(br->dev) || br->multicast_disabled ||
- !br->multicast_querier ||
- timer_pending(&br->multicast_querier_timer))
+ !br->multicast_querier)
return;
memset(&br_group.u, 0, sizeof(br_group.u));
- br_group.proto = htons(ETH_P_IP);
- __br_multicast_send_query(br, port, &br_group);
-
+ if (port ? (query == &port->ip4_query) :
+ (query == &br->ip4_query)) {
+ querier = &br->ip4_querier;
+ br_group.proto = htons(ETH_P_IP);
#if IS_ENABLED(CONFIG_IPV6)
- br_group.proto = htons(ETH_P_IPV6);
- __br_multicast_send_query(br, port, &br_group);
+ } else {
+ querier = &br->ip6_querier;
+ br_group.proto = htons(ETH_P_IPV6);
#endif
+ }
+
+ if (!querier || timer_pending(&querier->timer))
+ return;
+
+ __br_multicast_send_query(br, port, &br_group);
time = jiffies;
- time += sent < br->multicast_startup_query_count ?
+ time += query->startup_sent < br->multicast_startup_query_count ?
br->multicast_startup_query_interval :
br->multicast_query_interval;
- mod_timer(port ? &port->multicast_query_timer :
- &br->multicast_query_timer, time);
+ mod_timer(&query->timer, time);
}
-static void br_multicast_port_query_expired(unsigned long data)
+static void br_multicast_port_query_expired(struct net_bridge_port *port,
+ struct bridge_mcast_query *query)
{
- struct net_bridge_port *port = (void *)data;
struct net_bridge *br = port->br;
spin_lock(&br->multicast_lock);
port->state == BR_STATE_BLOCKING)
goto out;
- if (port->multicast_startup_queries_sent <
- br->multicast_startup_query_count)
- port->multicast_startup_queries_sent++;
+ if (query->startup_sent < br->multicast_startup_query_count)
+ query->startup_sent++;
- br_multicast_send_query(port->br, port,
- port->multicast_startup_queries_sent);
+ br_multicast_send_query(port->br, port, query);
out:
spin_unlock(&br->multicast_lock);
}
+static void br_ip4_multicast_port_query_expired(unsigned long data)
+{
+ struct net_bridge_port *port = (void *)data;
+
+ br_multicast_port_query_expired(port, &port->ip4_query);
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+static void br_ip6_multicast_port_query_expired(unsigned long data)
+{
+ struct net_bridge_port *port = (void *)data;
+
+ br_multicast_port_query_expired(port, &port->ip6_query);
+}
+#endif
+
void br_multicast_add_port(struct net_bridge_port *port)
{
port->multicast_router = 1;
setup_timer(&port->multicast_router_timer, br_multicast_router_expired,
(unsigned long)port);
- setup_timer(&port->multicast_query_timer,
- br_multicast_port_query_expired, (unsigned long)port);
+ setup_timer(&port->ip4_query.timer, br_ip4_multicast_port_query_expired,
+ (unsigned long)port);
+#if IS_ENABLED(CONFIG_IPV6)
+ setup_timer(&port->ip6_query.timer, br_ip6_multicast_port_query_expired,
+ (unsigned long)port);
+#endif
}
void br_multicast_del_port(struct net_bridge_port *port)
del_timer_sync(&port->multicast_router_timer);
}
-static void __br_multicast_enable_port(struct net_bridge_port *port)
+static void br_multicast_enable(struct bridge_mcast_query *query)
{
- port->multicast_startup_queries_sent = 0;
+ query->startup_sent = 0;
- if (try_to_del_timer_sync(&port->multicast_query_timer) >= 0 ||
- del_timer(&port->multicast_query_timer))
- mod_timer(&port->multicast_query_timer, jiffies);
+ if (try_to_del_timer_sync(&query->timer) >= 0 ||
+ del_timer(&query->timer))
+ mod_timer(&query->timer, jiffies);
}
void br_multicast_enable_port(struct net_bridge_port *port)
if (br->multicast_disabled || !netif_running(br->dev))
goto out;
- __br_multicast_enable_port(port);
+ br_multicast_enable(&port->ip4_query);
+#if IS_ENABLED(CONFIG_IPV6)
+ br_multicast_enable(&port->ip6_query);
+#endif
out:
spin_unlock(&br->multicast_lock);
if (!hlist_unhashed(&port->rlist))
hlist_del_init_rcu(&port->rlist);
del_timer(&port->multicast_router_timer);
- del_timer(&port->multicast_query_timer);
+ del_timer(&port->ip4_query.timer);
+#if IS_ENABLED(CONFIG_IPV6)
+ del_timer(&port->ip6_query.timer);
+#endif
spin_unlock(&br->multicast_lock);
}
}
#endif
-static void br_multicast_update_querier_timer(struct net_bridge *br,
- unsigned long max_delay)
+static void
+br_multicast_update_querier_timer(struct net_bridge *br,
+ struct bridge_mcast_querier *querier,
+ unsigned long max_delay)
{
- if (!timer_pending(&br->multicast_querier_timer))
- br->multicast_querier_delay_time = jiffies + max_delay;
+ if (!timer_pending(&querier->timer))
+ querier->delay_time = jiffies + max_delay;
- mod_timer(&br->multicast_querier_timer,
- jiffies + br->multicast_querier_interval);
+ mod_timer(&querier->timer, jiffies + br->multicast_querier_interval);
}
/*
static void br_multicast_query_received(struct net_bridge *br,
struct net_bridge_port *port,
+ struct bridge_mcast_querier *querier,
int saddr,
unsigned long max_delay)
{
if (saddr)
- br_multicast_update_querier_timer(br, max_delay);
- else if (timer_pending(&br->multicast_querier_timer))
+ br_multicast_update_querier_timer(br, querier, max_delay);
+ else if (timer_pending(&querier->timer))
return;
br_multicast_mark_router(br, port);
IGMPV3_MRC(ih3->code) * (HZ / IGMP_TIMER_SCALE) : 1;
}
- br_multicast_query_received(br, port, !!iph->saddr, max_delay);
+ br_multicast_query_received(br, port, &br->ip4_querier, !!iph->saddr,
+ max_delay);
if (!group)
goto out;
mld2q = (struct mld2_query *)icmp6_hdr(skb);
if (!mld2q->mld2q_nsrcs)
group = &mld2q->mld2q_mca;
- max_delay = mld2q->mld2q_mrc ? MLDV2_MRC(ntohs(mld2q->mld2q_mrc)) : 1;
+
+ max_delay = max(msecs_to_jiffies(MLDV2_MRC(ntohs(mld2q->mld2q_mrc))), 1UL);
}
- br_multicast_query_received(br, port, !ipv6_addr_any(&ip6h->saddr),
- max_delay);
+ br_multicast_query_received(br, port, &br->ip6_querier,
+ !ipv6_addr_any(&ip6h->saddr), max_delay);
if (!group)
goto out;
static void br_multicast_leave_group(struct net_bridge *br,
struct net_bridge_port *port,
- struct br_ip *group)
+ struct br_ip *group,
+ struct bridge_mcast_querier *querier,
+ struct bridge_mcast_query *query)
{
struct net_bridge_mdb_htable *mdb;
struct net_bridge_mdb_entry *mp;
spin_lock(&br->multicast_lock);
if (!netif_running(br->dev) ||
(port && port->state == BR_STATE_DISABLED) ||
- timer_pending(&br->multicast_querier_timer))
+ timer_pending(&querier->timer))
goto out;
mdb = mlock_dereference(br->mdb, br);
if (!mp)
goto out;
- if (br->multicast_querier &&
- !timer_pending(&br->multicast_querier_timer)) {
+ if (br->multicast_querier) {
__br_multicast_send_query(br, port, &mp->addr);
time = jiffies + br->multicast_last_member_count *
br->multicast_last_member_interval;
- mod_timer(port ? &port->multicast_query_timer :
- &br->multicast_query_timer, time);
+
+ mod_timer(&query->timer, time);
for (p = mlock_dereference(mp->ports, br);
p != NULL;
mod_timer(&mp->timer, time);
}
}
-
out:
spin_unlock(&br->multicast_lock);
}
__u16 vid)
{
struct br_ip br_group;
+ struct bridge_mcast_query *query = port ? &port->ip4_query :
+ &br->ip4_query;
if (ipv4_is_local_multicast(group))
return;
br_group.proto = htons(ETH_P_IP);
br_group.vid = vid;
- br_multicast_leave_group(br, port, &br_group);
+ br_multicast_leave_group(br, port, &br_group, &br->ip4_querier, query);
}
#if IS_ENABLED(CONFIG_IPV6)
__u16 vid)
{
struct br_ip br_group;
+ struct bridge_mcast_query *query = port ? &port->ip6_query :
+ &br->ip6_query;
+
if (!ipv6_is_transient_multicast(group))
return;
br_group.proto = htons(ETH_P_IPV6);
br_group.vid = vid;
- br_multicast_leave_group(br, port, &br_group);
+ br_multicast_leave_group(br, port, &br_group, &br->ip6_querier, query);
}
#endif
return 0;
}
-static void br_multicast_query_expired(unsigned long data)
+static void br_multicast_query_expired(struct net_bridge *br,
+ struct bridge_mcast_query *query)
+{
+ spin_lock(&br->multicast_lock);
+ if (query->startup_sent < br->multicast_startup_query_count)
+ query->startup_sent++;
+
+ br_multicast_send_query(br, NULL, query);
+ spin_unlock(&br->multicast_lock);
+}
+
+static void br_ip4_multicast_query_expired(unsigned long data)
{
struct net_bridge *br = (void *)data;
- spin_lock(&br->multicast_lock);
- if (br->multicast_startup_queries_sent <
- br->multicast_startup_query_count)
- br->multicast_startup_queries_sent++;
+ br_multicast_query_expired(br, &br->ip4_query);
+}
- br_multicast_send_query(br, NULL, br->multicast_startup_queries_sent);
+#if IS_ENABLED(CONFIG_IPV6)
+static void br_ip6_multicast_query_expired(unsigned long data)
+{
+ struct net_bridge *br = (void *)data;
- spin_unlock(&br->multicast_lock);
+ br_multicast_query_expired(br, &br->ip6_query);
}
+#endif
void br_multicast_init(struct net_bridge *br)
{
br->multicast_querier_interval = 255 * HZ;
br->multicast_membership_interval = 260 * HZ;
- br->multicast_querier_delay_time = 0;
+ br->ip4_querier.delay_time = 0;
+#if IS_ENABLED(CONFIG_IPV6)
+ br->ip6_querier.delay_time = 0;
+#endif
spin_lock_init(&br->multicast_lock);
setup_timer(&br->multicast_router_timer,
br_multicast_local_router_expired, 0);
- setup_timer(&br->multicast_querier_timer,
- br_multicast_querier_expired, (unsigned long)br);
- setup_timer(&br->multicast_query_timer, br_multicast_query_expired,
+ setup_timer(&br->ip4_querier.timer, br_ip4_multicast_querier_expired,
+ (unsigned long)br);
+ setup_timer(&br->ip4_query.timer, br_ip4_multicast_query_expired,
(unsigned long)br);
+#if IS_ENABLED(CONFIG_IPV6)
+ setup_timer(&br->ip6_querier.timer, br_ip6_multicast_querier_expired,
+ (unsigned long)br);
+ setup_timer(&br->ip6_query.timer, br_ip6_multicast_query_expired,
+ (unsigned long)br);
+#endif
}
-void br_multicast_open(struct net_bridge *br)
+static void __br_multicast_open(struct net_bridge *br,
+ struct bridge_mcast_query *query)
{
- br->multicast_startup_queries_sent = 0;
+ query->startup_sent = 0;
if (br->multicast_disabled)
return;
- mod_timer(&br->multicast_query_timer, jiffies);
+ mod_timer(&query->timer, jiffies);
+}
+
+void br_multicast_open(struct net_bridge *br)
+{
+ __br_multicast_open(br, &br->ip4_query);
+#if IS_ENABLED(CONFIG_IPV6)
+ __br_multicast_open(br, &br->ip6_query);
+#endif
}
void br_multicast_stop(struct net_bridge *br)
int i;
del_timer_sync(&br->multicast_router_timer);
- del_timer_sync(&br->multicast_querier_timer);
- del_timer_sync(&br->multicast_query_timer);
+ del_timer_sync(&br->ip4_querier.timer);
+ del_timer_sync(&br->ip4_query.timer);
+#if IS_ENABLED(CONFIG_IPV6)
+ del_timer_sync(&br->ip6_querier.timer);
+ del_timer_sync(&br->ip6_query.timer);
+#endif
spin_lock_bh(&br->multicast_lock);
mdb = mlock_dereference(br->mdb, br);
return err;
}
-static void br_multicast_start_querier(struct net_bridge *br)
+static void br_multicast_start_querier(struct net_bridge *br,
+ struct bridge_mcast_query *query)
{
struct net_bridge_port *port;
- br_multicast_open(br);
+ __br_multicast_open(br, query);
list_for_each_entry(port, &br->port_list, list) {
if (port->state == BR_STATE_DISABLED ||
port->state == BR_STATE_BLOCKING)
continue;
- __br_multicast_enable_port(port);
+ if (query == &br->ip4_query)
+ br_multicast_enable(&port->ip4_query);
+#if IS_ENABLED(CONFIG_IPV6)
+ else
+ br_multicast_enable(&port->ip6_query);
+#endif
}
}
goto rollback;
}
- br_multicast_start_querier(br);
+ br_multicast_start_querier(br, &br->ip4_query);
+#if IS_ENABLED(CONFIG_IPV6)
+ br_multicast_start_querier(br, &br->ip6_query);
+#endif
unlock:
spin_unlock_bh(&br->multicast_lock);
goto unlock;
max_delay = br->multicast_query_response_interval;
- if (!timer_pending(&br->multicast_querier_timer))
- br->multicast_querier_delay_time = jiffies + max_delay;
- br_multicast_start_querier(br);
+ if (!timer_pending(&br->ip4_querier.timer))
+ br->ip4_querier.delay_time = jiffies + max_delay;
+
+ br_multicast_start_querier(br, &br->ip4_query);
+
+#if IS_ENABLED(CONFIG_IPV6)
+ if (!timer_pending(&br->ip6_querier.timer))
+ br->ip6_querier.delay_time = jiffies + max_delay;
+
+ br_multicast_start_querier(br, &br->ip6_query);
+#endif
unlock:
spin_unlock_bh(&br->multicast_lock);
else
pv = br_get_vlan_info(br);
- if (!pv || bitmap_empty(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN))
+ if (!pv || bitmap_empty(pv->vlan_bitmap, VLAN_N_VID))
goto done;
af = nla_nest_start(skb, IFLA_AF_SPEC);
goto nla_put_failure;
pvid = br_get_pvid(pv);
- for_each_set_bit(vid, pv->vlan_bitmap, BR_VLAN_BITMAP_LEN) {
+ for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) {
vinfo.vid = vid;
vinfo.flags = 0;
if (vid == pvid)
__u16 vid;
};
+#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
+/* our own querier */
+struct bridge_mcast_query {
+ struct timer_list timer;
+ u32 startup_sent;
+};
+
+/* other querier */
+struct bridge_mcast_querier {
+ struct timer_list timer;
+ unsigned long delay_time;
+};
+#endif
+
struct net_port_vlans {
u16 port_idx;
u16 pvid;
#define BR_FLOOD 0x00000040
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
- u32 multicast_startup_queries_sent;
+ struct bridge_mcast_query ip4_query;
+#if IS_ENABLED(CONFIG_IPV6)
+ struct bridge_mcast_query ip6_query;
+#endif /* IS_ENABLED(CONFIG_IPV6) */
unsigned char multicast_router;
struct timer_list multicast_router_timer;
- struct timer_list multicast_query_timer;
struct hlist_head mglist;
struct hlist_node rlist;
#endif
u32 hash_max;
u32 multicast_last_member_count;
- u32 multicast_startup_queries_sent;
u32 multicast_startup_query_count;
unsigned long multicast_last_member_interval;
unsigned long multicast_query_interval;
unsigned long multicast_query_response_interval;
unsigned long multicast_startup_query_interval;
- unsigned long multicast_querier_delay_time;
spinlock_t multicast_lock;
struct net_bridge_mdb_htable __rcu *mdb;
struct hlist_head router_list;
struct timer_list multicast_router_timer;
- struct timer_list multicast_querier_timer;
- struct timer_list multicast_query_timer;
+ struct bridge_mcast_querier ip4_querier;
+ struct bridge_mcast_query ip4_query;
+#if IS_ENABLED(CONFIG_IPV6)
+ struct bridge_mcast_querier ip6_querier;
+ struct bridge_mcast_query ip6_query;
+#endif /* IS_ENABLED(CONFIG_IPV6) */
#endif
struct timer_list hello_timer;
timer_pending(&br->multicast_router_timer));
}
-static inline bool br_multicast_querier_exists(struct net_bridge *br)
+static inline bool
+__br_multicast_querier_exists(struct net_bridge *br,
+ struct bridge_mcast_querier *querier)
+{
+ return time_is_before_jiffies(querier->delay_time) &&
+ (br->multicast_querier || timer_pending(&querier->timer));
+}
+
+static inline bool br_multicast_querier_exists(struct net_bridge *br,
+ struct ethhdr *eth)
{
- return time_is_before_jiffies(br->multicast_querier_delay_time) &&
- (br->multicast_querier ||
- timer_pending(&br->multicast_querier_timer));
+ switch (eth->h_proto) {
+ case (htons(ETH_P_IP)):
+ return __br_multicast_querier_exists(br, &br->ip4_querier);
+#if IS_ENABLED(CONFIG_IPV6)
+ case (htons(ETH_P_IPV6)):
+ return __br_multicast_querier_exists(br, &br->ip6_querier);
+#endif
+ default:
+ return false;
+ }
}
#else
static inline int br_multicast_rcv(struct net_bridge *br,
{
return 0;
}
-static inline bool br_multicast_querier_exists(struct net_bridge *br)
+static inline bool br_multicast_querier_exists(struct net_bridge *br,
+ struct ethhdr *eth)
{
return false;
}
clear_bit(vid, v->vlan_bitmap);
v->num_vlans--;
- if (bitmap_empty(v->vlan_bitmap, BR_VLAN_BITMAP_LEN)) {
+ if (bitmap_empty(v->vlan_bitmap, VLAN_N_VID)) {
if (v->port_idx)
rcu_assign_pointer(v->parent.port->vlan_info, NULL);
else
{
smp_wmb();
v->pvid = 0;
- bitmap_zero(v->vlan_bitmap, BR_VLAN_BITMAP_LEN);
+ bitmap_zero(v->vlan_bitmap, VLAN_N_VID);
if (v->port_idx)
rcu_assign_pointer(v->parent.port->vlan_info, NULL);
else
if (new_index < 0)
new_index = skb_tx_hash(dev, skb);
- if (queue_index != new_index && sk) {
- struct dst_entry *dst =
- rcu_dereference_check(sk->sk_dst_cache, 1);
-
- if (dst && skb_dst(skb) == dst)
- sk_tx_queue_set(sk, queue_index);
-
- }
+ if (queue_index != new_index && sk &&
+ rcu_access_pointer(sk->sk_dst_cache))
+ sk_tx_queue_set(sk, queue_index);
queue_index = new_index;
}
return -EINVAL;
if ((creds->pid == task_tgid_vnr(current) ||
- ns_capable(current->nsproxy->pid_ns->user_ns, CAP_SYS_ADMIN)) &&
+ ns_capable(task_active_pid_ns(current)->user_ns, CAP_SYS_ADMIN)) &&
((uid_eq(uid, cred->uid) || uid_eq(uid, cred->euid) ||
uid_eq(uid, cred->suid)) || nsown_capable(CAP_SETUID)) &&
((gid_eq(gid, cred->gid) || gid_eq(gid, cred->egid) ||
return -EINVAL;
}
-static inline int ip_skb_dst_mtu(struct sk_buff *skb)
-{
- struct inet_sock *inet = skb->sk ? inet_sk(skb->sk) : NULL;
-
- return (inet && inet->pmtudisc == IP_PMTUDISC_PROBE) ?
- skb_dst(skb)->dev->mtu : dst_mtu(skb_dst(skb));
-}
-
static int ip_finish_output(struct sk_buff *skb)
{
#if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM)
struct ip_tunnel *tunnel;
const struct iphdr *iph;
- if (iptunnel_pull_header(skb, 0, tpi.proto))
- goto drop;
-
iph = ip_hdr(skb);
tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex, TUNNEL_NO_KEY,
iph->saddr, iph->daddr, 0);
if (tunnel) {
if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
goto drop;
+ if (iptunnel_pull_header(skb, 0, tpi.proto))
+ goto drop;
return ip_tunnel_rcv(tunnel, skb, &tpi, log_ecn_error);
}
flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos,
RT_SCOPE_UNIVERSE,
inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol,
- inet_sk_flowi_flags(sk) | FLOWI_FLAG_CAN_SLEEP,
+ inet_sk_flowi_flags(sk) | FLOWI_FLAG_CAN_SLEEP |
+ (inet->hdrincl ? FLOWI_FLAG_KNOWN_NH : 0),
daddr, saddr, 0, 0);
if (!inet->hdrincl) {
if (!skb)
goto wait_for_memory;
+ /*
+ * All packets are restored as if they have
+ * already been sent.
+ */
+ if (tp->repair)
+ TCP_SKB_CB(skb)->when = tcp_time_stamp;
+
/*
* Check whether we can use HW checksum.
*/
++ptr;
tp->rx_opt.rcv_tsval = ntohl(*ptr);
++ptr;
- tp->rx_opt.rcv_tsecr = ntohl(*ptr) - tp->tsoffset;
+ if (*ptr)
+ tp->rx_opt.rcv_tsecr = ntohl(*ptr) - tp->tsoffset;
+ else
+ tp->rx_opt.rcv_tsecr = 0;
return true;
}
return false;
}
tcp_parse_options(skb, &tp->rx_opt, 1, NULL);
- if (tp->rx_opt.saw_tstamp)
+ if (tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr)
tp->rx_opt.rcv_tsecr -= tp->tsoffset;
return true;
int saved_clamp = tp->rx_opt.mss_clamp;
tcp_parse_options(skb, &tp->rx_opt, 0, &foc);
- if (tp->rx_opt.saw_tstamp)
+ if (tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr)
tp->rx_opt.rcv_tsecr -= tp->tsoffset;
if (th->ack) {
int tcp_header_size;
int mss;
- skb = alloc_skb(MAX_TCP_HEADER + 15, sk_gfp_atomic(sk, GFP_ATOMIC));
+ skb = sock_wmalloc(sk, MAX_TCP_HEADER + 15, 1, GFP_ATOMIC);
if (unlikely(!skb)) {
dst_release(dst);
return NULL;
if (likely(!tp->repair))
tp->rcv_nxt = 0;
+ else
+ tp->rcv_tstamp = tcp_time_stamp;
tp->rcv_wup = tp->rcv_nxt;
tp->copied_seq = tp->rcv_nxt;
static int xfrm4_tunnel_check_size(struct sk_buff *skb)
{
int mtu, ret = 0;
- struct dst_entry *dst;
if (IPCB(skb)->flags & IPSKB_XFRM_TUNNEL_SIZE)
goto out;
if (!(ip_hdr(skb)->frag_off & htons(IP_DF)) || skb->local_df)
goto out;
- dst = skb_dst(skb);
- mtu = dst_mtu(dst);
+ mtu = dst_mtu(skb_dst(skb));
if (skb->len > mtu) {
if (skb->sk)
- ip_local_error(skb->sk, EMSGSIZE, ip_hdr(skb)->daddr,
- inet_sk(skb->sk)->inet_dport, mtu);
+ xfrm_local_error(skb, mtu);
else
icmp_send(skb, ICMP_DEST_UNREACH,
ICMP_FRAG_NEEDED, htonl(mtu));
x->outer_mode->afinfo->output_finish,
!(IPCB(skb)->flags & IPSKB_REROUTED));
}
+
+void xfrm4_local_error(struct sk_buff *skb, u32 mtu)
+{
+ struct iphdr *hdr;
+
+ hdr = skb->encapsulation ? inner_ip_hdr(skb) : ip_hdr(skb);
+ ip_local_error(skb->sk, EMSGSIZE, hdr->daddr,
+ inet_sk(skb->sk)->inet_dport, mtu);
+}
.extract_input = xfrm4_extract_input,
.extract_output = xfrm4_extract_output,
.transport_finish = xfrm4_transport_finish,
+ .local_error = xfrm4_local_error,
};
void __init xfrm4_state_init(void)
if (ifp->flags & IFA_F_OPTIMISTIC)
addr_flags |= IFA_F_OPTIMISTIC;
- ift = !max_addresses ||
- ipv6_count_addresses(idev) < max_addresses ?
- ipv6_add_addr(idev, &addr, NULL, tmp_plen,
- ipv6_addr_scope(&addr), addr_flags,
- tmp_valid_lft, tmp_prefered_lft) : NULL;
- if (IS_ERR_OR_NULL(ift)) {
+ ift = ipv6_add_addr(idev, &addr, NULL, tmp_plen,
+ ipv6_addr_scope(&addr), addr_flags,
+ tmp_valid_lft, tmp_prefered_lft);
+ if (IS_ERR(ift)) {
in6_ifa_put(ifp);
in6_dev_put(idev);
pr_info("%s: retry temporary address regeneration\n", __func__);
ipv6_push_nfrag_opts(skb, &opt.ops, &proto, NULL);
}
+ if (likely(!skb->encapsulation)) {
+ skb_reset_inner_headers(skb);
+ skb->encapsulation = 1;
+ }
+
skb_push(skb, gre_hlen);
skb_reset_network_header(skb);
skb_set_transport_header(skb, sizeof(*ipv6h));
hdr->saddr = fl6->saddr;
hdr->daddr = *first_hop;
+ skb->protocol = htons(ETH_P_IPV6);
skb->priority = sk->sk_priority;
skb->mark = sk->sk_mark;
/* initialize protocol header pointer */
skb->transport_header = skb->network_header + fragheaderlen;
+ skb->protocol = htons(ETH_P_IPV6);
skb->ip_summed = CHECKSUM_PARTIAL;
skb->csum = 0;
}
/*
* Fill in the control structures
*/
+ skb->protocol = htons(ETH_P_IPV6);
skb->ip_summed = CHECKSUM_NONE;
skb->csum = 0;
/* reserve for fragmentation and ipsec header */
init_tel_txopt(&opt, encap_limit);
ipv6_push_nfrag_opts(skb, &opt.ops, &proto, NULL);
}
+
+ if (likely(!skb->encapsulation)) {
+ skb_reset_inner_headers(skb);
+ skb->encapsulation = 1;
+ }
+
skb_push(skb, sizeof(struct ipv6hdr));
skb_reset_network_header(skb);
ipv6h = ipv6_hdr(skb);
if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts))
return;
- if (!ndopts.nd_opts_rh)
+ if (!ndopts.nd_opts_rh) {
+ ip6_redirect_no_header(skb, dev_net(skb->dev), 0, 0);
return;
+ }
hdr = (u8 *)ndopts.nd_opts_rh;
hdr += 8;
goto error;
skb_reserve(skb, hlen);
+ skb->protocol = htons(ETH_P_IPV6);
skb->priority = sk->sk_priority;
skb->mark = sk->sk_mark;
skb_dst_set(skb, &rt->dst);
ipv6_hdr(head)->payload_len = htons(payload_len);
ipv6_change_dsfield(ipv6_hdr(head), 0xff, ecn);
IP6CB(head)->nhoff = nhoff;
+ IP6CB(head)->flags |= IP6SKB_FRAGMENTED;
/* Yes, and fold redundant checksum back. 8) */
if (head->ip_summed == CHECKSUM_COMPLETE)
struct net *net = dev_net(skb_dst(skb)->dev);
int evicted;
+ if (IP6CB(skb)->flags & IP6SKB_FRAGMENTED)
+ goto fail_hdr;
+
IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_REASMREQDS);
/* Jumbo payload inhibits frag. header */
ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_REASMOKS);
IP6CB(skb)->nhoff = (u8 *)fhdr - skb_network_header(skb);
+ IP6CB(skb)->flags |= IP6SKB_FRAGMENTED;
return 1;
}
}
EXPORT_SYMBOL_GPL(ip6_redirect);
+void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif,
+ u32 mark)
+{
+ const struct ipv6hdr *iph = ipv6_hdr(skb);
+ const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb);
+ struct dst_entry *dst;
+ struct flowi6 fl6;
+
+ memset(&fl6, 0, sizeof(fl6));
+ fl6.flowi6_oif = oif;
+ fl6.flowi6_mark = mark;
+ fl6.flowi6_flags = 0;
+ fl6.daddr = msg->dest;
+ fl6.saddr = iph->daddr;
+
+ dst = ip6_route_output(net, NULL, &fl6);
+ if (!dst->error)
+ rt6_do_redirect(dst, NULL, skb);
+ dst_release(dst);
+}
+
void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
{
ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark);
const struct iphdr *iph;
struct ip_tunnel *tunnel;
- if (iptunnel_pull_header(skb, 0, tpi.proto))
- goto drop;
-
iph = ip_hdr(skb);
-
tunnel = ipip6_tunnel_lookup(dev_net(skb->dev), skb->dev,
iph->saddr, iph->daddr);
if (tunnel != NULL) {
if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
goto drop;
+ if (iptunnel_pull_header(skb, 0, tpi.proto))
+ goto drop;
return ip_tunnel_rcv(tunnel, skb, &tpi, log_ecn_error);
}
ttl = iph6->hop_limit;
tos = INET_ECN_encapsulate(tos, ipv6_get_dsfield(iph6));
+ if (likely(!skb->encapsulation)) {
+ skb_reset_inner_headers(skb);
+ skb->encapsulation = 1;
+ }
+
err = iptunnel_xmit(dev_net(dev), rt, skb, fl4.saddr, fl4.daddr,
IPPROTO_IPV6, tos, ttl, df);
iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
struct sock *sk = skb->sk;
if (sk) {
- proto = sk->sk_protocol;
+ if (sk->sk_family != AF_INET6)
+ return 0;
+ proto = sk->sk_protocol;
if (proto == IPPROTO_UDP || proto == IPPROTO_RAW)
return inet6_sk(sk)->dontfrag;
}
ipv6_local_rxpmtu(sk, &fl6, mtu);
}
-static void xfrm6_local_error(struct sk_buff *skb, u32 mtu)
+void xfrm6_local_error(struct sk_buff *skb, u32 mtu)
{
struct flowi6 fl6;
+ const struct ipv6hdr *hdr;
struct sock *sk = skb->sk;
+ hdr = skb->encapsulation ? inner_ipv6_hdr(skb) : ipv6_hdr(skb);
fl6.fl6_dport = inet_sk(sk)->inet_dport;
- fl6.daddr = ipv6_hdr(skb)->daddr;
+ fl6.daddr = hdr->daddr;
ipv6_local_error(sk, EMSGSIZE, &fl6, mtu);
}
if (xfrm6_local_dontfrag(skb))
xfrm6_local_rxpmtu(skb, mtu);
else if (skb->sk)
- xfrm6_local_error(skb, mtu);
+ xfrm_local_error(skb, mtu);
else
icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
ret = -EMSGSIZE;
{
struct dst_entry *dst = skb_dst(skb);
struct xfrm_state *x = dst->xfrm;
- int mtu = ip6_skb_dst_mtu(skb);
+ int mtu;
+
+ if (skb->protocol == htons(ETH_P_IPV6))
+ mtu = ip6_skb_dst_mtu(skb);
+ else
+ mtu = dst_mtu(skb_dst(skb));
if (skb->len > mtu && xfrm6_local_dontfrag(skb)) {
xfrm6_local_rxpmtu(skb, mtu);
return -EMSGSIZE;
} else if (!skb->local_df && skb->len > mtu && skb->sk) {
- xfrm6_local_error(skb, mtu);
+ xfrm_local_error(skb, mtu);
return -EMSGSIZE;
}
.extract_input = xfrm6_extract_input,
.extract_output = xfrm6_extract_output,
.transport_finish = xfrm6_transport_finish,
+ .local_error = xfrm6_local_error,
};
int __init xfrm6_state_init(void)
static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
const u8 *bssid, const int beacon_int,
- struct ieee80211_channel *chan,
+ struct cfg80211_chan_def *req_chandef,
const u32 basic_rates,
const u16 capability, u64 tsf,
bool creator)
u32 bss_change;
u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
struct cfg80211_chan_def chandef;
+ struct ieee80211_channel *chan;
struct beacon_data *presp;
int frame_len;
sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;
- chandef = ifibss->chandef;
+ /* make a copy of the chandef, it could be modified below. */
+ chandef = *req_chandef;
+ chan = chandef.chan;
if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) {
chandef.width = NL80211_CHAN_WIDTH_20;
chandef.center_freq1 = chan->center_freq;
struct cfg80211_bss *cbss =
container_of((void *)bss, struct cfg80211_bss, priv);
struct ieee80211_supported_band *sband;
+ struct cfg80211_chan_def chandef;
u32 basic_rates;
int i, j;
u16 beacon_int = cbss->beacon_interval;
const struct cfg80211_bss_ies *ies;
+ enum nl80211_channel_type chan_type;
u64 tsf;
sdata_assert_lock(sdata);
if (beacon_int < 10)
beacon_int = 10;
+ switch (sdata->u.ibss.chandef.width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ case NL80211_CHAN_WIDTH_20:
+ case NL80211_CHAN_WIDTH_40:
+ chan_type = cfg80211_get_chandef_type(&sdata->u.ibss.chandef);
+ cfg80211_chandef_create(&chandef, cbss->channel, chan_type);
+ break;
+ case NL80211_CHAN_WIDTH_5:
+ case NL80211_CHAN_WIDTH_10:
+ cfg80211_chandef_create(&chandef, cbss->channel,
+ NL80211_CHAN_WIDTH_20_NOHT);
+ chandef.width = sdata->u.ibss.chandef.width;
+ break;
+ default:
+ /* fall back to 20 MHz for unsupported modes */
+ cfg80211_chandef_create(&chandef, cbss->channel,
+ NL80211_CHAN_WIDTH_20_NOHT);
+ break;
+ }
+
sband = sdata->local->hw.wiphy->bands[cbss->channel->band];
basic_rates = 0;
__ieee80211_sta_join_ibss(sdata, cbss->bssid,
beacon_int,
- cbss->channel,
+ &chandef,
basic_rates,
cbss->capability,
tsf, false);
sdata->drop_unencrypted = 0;
__ieee80211_sta_join_ibss(sdata, bssid, sdata->vif.bss_conf.beacon_int,
- ifibss->chandef.chan, ifibss->basic_rates,
+ &ifibss->chandef, ifibss->basic_rates,
capability, 0, true);
}
clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED |
BSS_CHANGED_IBSS);
+ ieee80211_vif_release_channel(sdata);
synchronize_rcu();
kfree(presp);
if (sband->band != IEEE80211_BAND_2GHZ)
return;
+ if (!(mp->hw->flags & IEEE80211_HW_SUPPORTS_HT_CCK_RATES))
+ return;
+
mi->cck_supported = 0;
mi->cck_supported_short = 0;
for (i = 0; i < 4; i++) {
EXPORT_SYMBOL(genl_unregister_ops);
/**
- * genl_register_family - register a generic netlink family
+ * __genl_register_family - register a generic netlink family
* @family: generic netlink family
*
* Registers the specified family after validating it first. Only one
*
* Return 0 on success or a negative error code.
*/
-int genl_register_family(struct genl_family *family)
+int __genl_register_family(struct genl_family *family)
{
int err = -EINVAL;
errout:
return err;
}
-EXPORT_SYMBOL(genl_register_family);
+EXPORT_SYMBOL(__genl_register_family);
/**
- * genl_register_family_with_ops - register a generic netlink family
+ * __genl_register_family_with_ops - register a generic netlink family
* @family: generic netlink family
* @ops: operations to be registered
* @n_ops: number of elements to register
*
* Return 0 on success or a negative error code.
*/
-int genl_register_family_with_ops(struct genl_family *family,
+int __genl_register_family_with_ops(struct genl_family *family,
struct genl_ops *ops, size_t n_ops)
{
int err, i;
- err = genl_register_family(family);
+ err = __genl_register_family(family);
if (err)
return err;
genl_unregister_family(family);
return err;
}
-EXPORT_SYMBOL(genl_register_family_with_ops);
+EXPORT_SYMBOL(__genl_register_family_with_ops);
/**
* genl_unregister_family - unregister generic netlink family
}
EXPORT_SYMBOL(genlmsg_put);
+static int genl_lock_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct genl_ops *ops = cb->data;
+ int rc;
+
+ genl_lock();
+ rc = ops->dumpit(skb, cb);
+ genl_unlock();
+ return rc;
+}
+
+static int genl_lock_done(struct netlink_callback *cb)
+{
+ struct genl_ops *ops = cb->data;
+ int rc = 0;
+
+ if (ops->done) {
+ genl_lock();
+ rc = ops->done(cb);
+ genl_unlock();
+ }
+ return rc;
+}
+
static int genl_family_rcv_msg(struct genl_family *family,
struct sk_buff *skb,
struct nlmsghdr *nlh)
return -EPERM;
if ((nlh->nlmsg_flags & NLM_F_DUMP) == NLM_F_DUMP) {
- struct netlink_dump_control c = {
- .dump = ops->dumpit,
- .done = ops->done,
- };
+ int rc;
if (ops->dumpit == NULL)
return -EOPNOTSUPP;
- return netlink_dump_start(net->genl_sock, skb, nlh, &c);
+ if (!family->parallel_ops) {
+ struct netlink_dump_control c = {
+ .module = family->module,
+ .data = ops,
+ .dump = genl_lock_dumpit,
+ .done = genl_lock_done,
+ };
+
+ genl_unlock();
+ rc = __netlink_dump_start(net->genl_sock, skb, nlh, &c);
+ genl_lock();
+
+ } else {
+ struct netlink_dump_control c = {
+ .module = family->module,
+ .dump = ops->dumpit,
+ .done = ops->done,
+ };
+
+ rc = __netlink_dump_start(net->genl_sock, skb, nlh, &c);
+ }
+
+ return rc;
}
if (ops->doit == NULL)
struct net *net = sock_net(skb->sk);
int chains_to_skip = cb->args[0];
int fams_to_skip = cb->args[1];
- bool need_locking = chains_to_skip || fams_to_skip;
-
- if (need_locking)
- genl_lock();
for (i = chains_to_skip; i < GENL_FAM_TAB_SIZE; i++) {
n = 0;
cb->args[0] = i;
cb->args[1] = n;
- if (need_locking)
- genl_unlock();
-
return skb->len;
}
if (po->tp_version == TPACKET_V3) {
lv = sizeof(struct tpacket_stats_v3);
+ st.stats3.tp_packets += st.stats3.tp_drops;
data = &st.stats3;
} else {
lv = sizeof(struct tpacket_stats);
+ st.stats1.tp_packets += st.stats1.tp_drops;
data = &st.stats1;
}
pgfrom_base -= copy;
vto = kmap_atomic(*pgto);
- vfrom = kmap_atomic(*pgfrom);
- memmove(vto + pgto_base, vfrom + pgfrom_base, copy);
+ if (*pgto != *pgfrom) {
+ vfrom = kmap_atomic(*pgfrom);
+ memcpy(vto + pgto_base, vfrom + pgfrom_base, copy);
+ kunmap_atomic(vfrom);
+ } else
+ memmove(vto + pgto_base, vto + pgfrom_base, copy);
flush_dcache_page(*pgto);
- kunmap_atomic(vfrom);
kunmap_atomic(vto);
} while ((len -= copy) != 0);
/* Accept only ACK or NACK message */
if (unlikely(msg_errcode(msg))) {
sock->state = SS_DISCONNECTING;
- sk->sk_err = -ECONNREFUSED;
+ sk->sk_err = ECONNREFUSED;
retval = TIPC_OK;
break;
}
res = auto_connect(sock, msg);
if (res) {
sock->state = SS_DISCONNECTING;
- sk->sk_err = res;
+ sk->sk_err = -res;
retval = TIPC_OK;
break;
}
hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
NL80211_CMD_NEW_KEY);
- if (IS_ERR(hdr))
- return PTR_ERR(hdr);
+ if (!hdr)
+ return -ENOBUFS;
cookie.msg = msg;
cookie.idx = key_idx;
NL80211_CMD_TESTMODE);
struct nlattr *tmdata;
+ if (!hdr)
+ break;
+
if (nla_put_u32(skb, NL80211_ATTR_WIPHY, phy_idx)) {
genlmsg_cancel(skb, hdr);
break;
hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
NL80211_CMD_REMAIN_ON_CHANNEL);
-
- if (IS_ERR(hdr)) {
- err = PTR_ERR(hdr);
+ if (!hdr) {
+ err = -ENOBUFS;
goto free_msg;
}
hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
NL80211_CMD_FRAME);
-
- if (IS_ERR(hdr)) {
- err = PTR_ERR(hdr);
+ if (!hdr) {
+ err = -ENOBUFS;
goto free_msg;
}
}
hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
NL80211_CMD_PROBE_CLIENT);
-
- if (IS_ERR(hdr)) {
- err = PTR_ERR(hdr);
+ if (!hdr) {
+ err = -ENOBUFS;
goto free_msg;
}
struct net_device *dev, u16 reason, bool wextev)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
- int err;
+ int err = 0;
ASSERT_WDEV_LOCK(wdev);
kfree(wdev->connect_keys);
wdev->connect_keys = NULL;
- if (wdev->conn) {
+ if (wdev->conn)
err = cfg80211_sme_disconnect(wdev, reason);
- } else if (!rdev->ops->disconnect) {
+ else if (!rdev->ops->disconnect)
cfg80211_mlme_down(rdev, dev);
- err = 0;
- } else {
+ else if (wdev->current_bss)
err = rdev_disconnect(rdev, dev, reason);
- }
return err;
}
return inner_mode->afinfo->extract_output(x, skb);
}
+void xfrm_local_error(struct sk_buff *skb, int mtu)
+{
+ unsigned int proto;
+ struct xfrm_state_afinfo *afinfo;
+
+ if (skb->protocol == htons(ETH_P_IP))
+ proto = AF_INET;
+ else if (skb->protocol == htons(ETH_P_IPV6))
+ proto = AF_INET6;
+ else
+ return;
+
+ afinfo = xfrm_state_get_afinfo(proto);
+ if (!afinfo)
+ return;
+
+ afinfo->local_error(skb, mtu);
+ xfrm_state_put_afinfo(afinfo);
+}
+
EXPORT_SYMBOL_GPL(xfrm_output);
EXPORT_SYMBOL_GPL(xfrm_inner_extract_output);
+EXPORT_SYMBOL_GPL(xfrm_local_error);
{
struct sk_buff *skb;
- while ((skb = skb_dequeue(list)) != NULL) {
- dev_put(skb->dev);
+ while ((skb = skb_dequeue(list)) != NULL)
kfree_skb(skb);
- }
}
/* Rule must be locked. Release descentant resources, announce
struct sk_buff *skb;
struct sock *sk;
struct dst_entry *dst;
- struct net_device *dev;
struct xfrm_policy *pol = (struct xfrm_policy *)arg;
struct xfrm_policy_queue *pq = &pol->polq;
struct flowi fl;
dst = xfrm_lookup(xp_net(pol), skb_dst(skb)->path,
&fl, skb->sk, 0);
if (IS_ERR(dst)) {
- dev_put(skb->dev);
kfree_skb(skb);
continue;
}
skb_dst_drop(skb);
skb_dst_set(skb, dst);
- dev = skb->dev;
err = dst_output(skb);
- dev_put(dev);
}
return;
}
skb_dst_force(skb);
- dev_hold(skb->dev);
spin_lock_bh(&pq->hold_queue.lock);
static unsigned int xfrm_state_hashmax __read_mostly = 1 * 1024 * 1024;
-static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family);
-static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo);
-
static inline unsigned int xfrm_dst_hash(struct net *net,
const xfrm_address_t *daddr,
const xfrm_address_t *saddr,
}
EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
-static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family)
+struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family)
{
struct xfrm_state_afinfo *afinfo;
if (unlikely(family >= NPROTO))
return afinfo;
}
-static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
+void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
{
rcu_read_unlock();
}
#endif /* CONFIG_PNP */
-#ifdef OPTi93X
-#define DEV_NAME "opti93x"
-#else
-#define DEV_NAME "opti92x"
-#endif
+#define DEV_NAME KBUILD_MODNAME
static char * snd_opti9xx_names[] = {
"unknown",
static struct pnp_card_driver opti9xx_pnpc_driver = {
.flags = PNP_DRIVER_RES_DISABLE,
- .name = "opti9xx",
+ .name = DEV_NAME,
.id_table = snd_opti9xx_pnpids,
.probe = snd_opti9xx_pnp_probe,
.remove = snd_opti9xx_pnp_remove,
struct snd_pcm_chmap *chmap;
struct snd_kcontrol *kctl;
int i;
+
+ if (!codec->pcm_info[pin_idx].pcm)
+ break;
err = snd_pcm_add_chmap_ctls(codec->pcm_info[pin_idx].pcm,
SNDRV_PCM_STREAM_PLAYBACK,
NULL, 0, pin_idx, &chmap);
static const struct snd_pci_quirk alc662_fixup_tbl[] = {
SND_PCI_QUIRK(0x1019, 0x9087, "ECS", ALC662_FIXUP_ASUS_MODE2),
+ SND_PCI_QUIRK(0x1025, 0x022f, "Acer Aspire One", ALC662_FIXUP_INV_DMIC),
SND_PCI_QUIRK(0x1025, 0x0308, "Acer Aspire 8942G", ALC662_FIXUP_ASPIRE),
SND_PCI_QUIRK(0x1025, 0x031c, "Gateway NV79", ALC662_FIXUP_SKU_IGNORE),
SND_PCI_QUIRK(0x1025, 0x0349, "eMachines eM250", ALC662_FIXUP_INV_DMIC),