Merge b7769c45b8
("Merge tag 'rtc-5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux") into android-mainline
Steps on the way to 5.10-rc1 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Change-Id: I8fb3e354b078fcccac88a148c84e9ee4c39118f8
This commit is contained in:
@@ -114,6 +114,9 @@ properties:
|
||||
- const: renesas,r1ex24128
|
||||
- const: atmel,24c128
|
||||
|
||||
label:
|
||||
description: Descriptive name of the EEPROM.
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
|
@@ -9,12 +9,18 @@ title: Freescale Low Power Inter IC (LPI2C) for i.MX
|
||||
maintainers:
|
||||
- Anson Huang <Anson.Huang@nxp.com>
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/i2c/i2c-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- fsl,imx7ulp-lpi2c
|
||||
- fsl,imx8qxp-lpi2c
|
||||
- fsl,imx8qm-lpi2c
|
||||
oneOf:
|
||||
- enum:
|
||||
- fsl,imx7ulp-lpi2c
|
||||
- fsl,imx8qm-lpi2c
|
||||
- items:
|
||||
- const: fsl,imx8qxp-lpi2c
|
||||
- const: fsl,imx7ulp-lpi2c
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
@@ -22,23 +28,34 @@ properties:
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
assigned-clock-parents: true
|
||||
assigned-clock-rates: true
|
||||
assigned-clocks: true
|
||||
clock-frequency: true
|
||||
|
||||
clock-names:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
|
||||
additionalProperties: false
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/imx7ulp-clock.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
lpi2c7@40a50000 {
|
||||
i2c@40a50000 {
|
||||
compatible = "fsl,imx7ulp-lpi2c";
|
||||
reg = <0x40A50000 0x10000>;
|
||||
interrupt-parent = <&intc>;
|
||||
|
@@ -9,6 +9,9 @@ title: Freescale Inter IC (I2C) and High Speed Inter IC (HS-I2C) for i.MX
|
||||
maintainers:
|
||||
- Wolfram Sang <wolfram@the-dreams.de>
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/i2c/i2c-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
@@ -18,6 +21,9 @@ properties:
|
||||
- items:
|
||||
- const: fsl,imx35-i2c
|
||||
- const: fsl,imx1-i2c
|
||||
- items:
|
||||
- const: fsl,imx7d-i2c
|
||||
- const: fsl,imx21-i2c
|
||||
- items:
|
||||
- enum:
|
||||
- fsl,imx25-i2c
|
||||
@@ -75,7 +81,7 @@ required:
|
||||
- interrupts
|
||||
- clocks
|
||||
|
||||
additionalProperties: false
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
@@ -87,6 +87,11 @@ wants to support one of the below features, it should adapt these bindings.
|
||||
this information to detect a stalled bus more reliably, for example.
|
||||
Can not be combined with 'multi-master'.
|
||||
|
||||
- smbus
|
||||
states that additional SMBus restrictions and features apply to this bus.
|
||||
Examples of features are SMBusHostNotify and SMBusAlert. Examples of
|
||||
restrictions are more reserved addresses and timeout definitions.
|
||||
|
||||
Required properties (per child device)
|
||||
--------------------------------------
|
||||
|
||||
|
@@ -17,9 +17,13 @@ properties:
|
||||
pattern: "^i2c@[0-9a-f]+$"
|
||||
|
||||
compatible:
|
||||
enum:
|
||||
- ingenic,jz4780-i2c
|
||||
- ingenic,x1000-i2c
|
||||
oneOf:
|
||||
- enum:
|
||||
- ingenic,jz4770-i2c
|
||||
- ingenic,x1000-i2c
|
||||
- items:
|
||||
- const: ingenic,jz4780-i2c
|
||||
- const: ingenic,jz4770-i2c
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
@@ -60,7 +64,7 @@ examples:
|
||||
#include <dt-bindings/dma/jz4780-dma.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c@10054000 {
|
||||
compatible = "ingenic,jz4780-i2c";
|
||||
compatible = "ingenic,jz4780-i2c", "ingenic,jz4770-i2c";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x10054000 0x1000>;
|
||||
|
42
Documentation/devicetree/bindings/i2c/mellanox,i2c-mlxbf.txt
Normal file
42
Documentation/devicetree/bindings/i2c/mellanox,i2c-mlxbf.txt
Normal file
@@ -0,0 +1,42 @@
|
||||
Device tree configuration for the Mellanox I2C SMBus on BlueField SoCs
|
||||
|
||||
Required Properties:
|
||||
|
||||
- compatible : should be "mellanox,i2c-mlxbf1" or "mellanox,i2c-mlxbf2".
|
||||
|
||||
- reg : address offset and length of the device registers. The
|
||||
registers consist of the following set of resources:
|
||||
1) Smbus block registers.
|
||||
2) Cause master registers.
|
||||
3) Cause slave registers.
|
||||
4) Cause coalesce registers (if compatible isn't set
|
||||
to "mellanox,i2c-mlxbf1").
|
||||
|
||||
- interrupts : interrupt number.
|
||||
|
||||
Optional Properties:
|
||||
|
||||
- clock-frequency : bus frequency used to configure timing registers;
|
||||
allowed values are 100000, 400000 and 1000000;
|
||||
those are expressed in Hz. Default is 100000.
|
||||
|
||||
Example:
|
||||
|
||||
i2c@2804000 {
|
||||
compatible = "mellanox,i2c-mlxbf1";
|
||||
reg = <0x02804000 0x800>,
|
||||
<0x02801200 0x020>,
|
||||
<0x02801260 0x020>;
|
||||
interrupts = <57>;
|
||||
clock-frequency = <100000>;
|
||||
};
|
||||
|
||||
i2c@2808800 {
|
||||
compatible = "mellanox,i2c-mlxbf2";
|
||||
reg = <0x02808800 0x600>,
|
||||
<0x02808e00 0x020>,
|
||||
<0x02808e20 0x020>,
|
||||
<0x02808e40 0x010>;
|
||||
interrupts = <57>;
|
||||
clock-frequency = <400000>;
|
||||
};
|
@@ -0,0 +1,64 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/rtc/microcrystal,rv3032.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Microchip RV-3032 RTC Device Tree Bindings
|
||||
|
||||
allOf:
|
||||
- $ref: "rtc.yaml#"
|
||||
|
||||
maintainers:
|
||||
- Alexandre Belloni <alexandre.belloni@bootlin.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: microcrystal,rv3032
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
start-year: true
|
||||
|
||||
trickle-resistor-ohms:
|
||||
enum:
|
||||
- 1000
|
||||
- 2000
|
||||
- 7000
|
||||
- 11000
|
||||
|
||||
trickle-voltage-millivolt:
|
||||
enum:
|
||||
- 1750
|
||||
- 3000
|
||||
- 4400
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
rtc@51 {
|
||||
compatible = "microcrystal,rv3032";
|
||||
reg = <0x51>;
|
||||
status = "okay";
|
||||
pinctrl-0 = <&rtc_nint_pins>;
|
||||
interrupts-extended = <&gpio1 16 IRQ_TYPE_LEVEL_HIGH>;
|
||||
trickle-resistor-ohms = <7000>;
|
||||
trickle-voltage-millivolt = <1750>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
@@ -31,9 +31,16 @@ Optional properties:
|
||||
Selected resistor for trickle charger
|
||||
Possible values are 250, 2000, 4000
|
||||
Should be given if trickle charger should be enabled
|
||||
- trickle-diode-disable : ds1339, ds1340 and ds 1388 only
|
||||
- aux-voltage-chargeable: ds1339, ds1340, ds1388 and rx8130 only
|
||||
Tells whether the battery/supercap of the RTC (if any) is
|
||||
chargeable or not.
|
||||
Possible values are 0 (not chargeable), 1 (chargeable)
|
||||
|
||||
Deprecated properties:
|
||||
- trickle-diode-disable : ds1339, ds1340 and ds1388 only
|
||||
Do not use internal trickle charger diode
|
||||
Should be given if internal trickle charger diode should be disabled
|
||||
(superseded by aux-voltage-chargeable)
|
||||
|
||||
Example:
|
||||
ds1339: rtc@68 {
|
||||
|
@@ -17,6 +17,15 @@ properties:
|
||||
$nodename:
|
||||
pattern: "^rtc(@.*|-[0-9a-f])*$"
|
||||
|
||||
aux-voltage-chargeable:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum: [0, 1]
|
||||
description: |
|
||||
Tells whether the battery/supercap of the RTC (if any) is
|
||||
chargeable or not:
|
||||
0: not chargeable
|
||||
1: chargeable
|
||||
|
||||
quartz-load-femtofarads:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
@@ -35,6 +44,7 @@ properties:
|
||||
description:
|
||||
Do not use internal trickle charger diode. Should be given if
|
||||
internal trickle charger diode should be disabled.
|
||||
deprecated: true
|
||||
|
||||
trickle-resistor-ohms:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
@@ -42,6 +52,12 @@ properties:
|
||||
Selected resistor for trickle charger. Should be given
|
||||
if trickle charger should be enabled.
|
||||
|
||||
trickle-voltage-millivolt:
|
||||
description:
|
||||
Selected voltage for trickle charger. Should be given
|
||||
if trickle charger should be enabled and the trickle voltage is different
|
||||
from the RTC main power supply.
|
||||
|
||||
wakeup-source:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
|
@@ -163,14 +163,14 @@ Mount Options
|
||||
to the default VFS implementation if this option is used.
|
||||
|
||||
recover_session=<no|clean>
|
||||
Set auto reconnect mode in the case where the client is blacklisted. The
|
||||
Set auto reconnect mode in the case where the client is blocklisted. The
|
||||
available modes are "no" and "clean". The default is "no".
|
||||
|
||||
* no: never attempt to reconnect when client detects that it has been
|
||||
blacklisted. Operations will generally fail after being blacklisted.
|
||||
blocklisted. Operations will generally fail after being blocklisted.
|
||||
|
||||
* clean: client reconnects to the ceph cluster automatically when it
|
||||
detects that it has been blacklisted. During reconnect, client drops
|
||||
detects that it has been blocklisted. During reconnect, client drops
|
||||
dirty data/metadata, invalidates page caches and writable file handles.
|
||||
After reconnect, file locks become stale because the MDS loses track
|
||||
of them. If an inode contains any stale file locks, read/write on the
|
||||
|
@@ -44,6 +44,7 @@ Supported adapters:
|
||||
* Intel Tiger Lake (PCH)
|
||||
* Intel Jasper Lake (SOC)
|
||||
* Intel Emmitsburg (PCH)
|
||||
* Intel Alder Lake (PCH)
|
||||
|
||||
Datasheets: Publicly available at the Intel website
|
||||
|
||||
|
@@ -47,6 +47,7 @@ Slave I2C
|
||||
|
||||
slave-interface
|
||||
slave-eeprom-backend
|
||||
slave-testunit-backend
|
||||
|
||||
Advanced topics
|
||||
===============
|
||||
|
69
Documentation/i2c/slave-testunit-backend.rst
Normal file
69
Documentation/i2c/slave-testunit-backend.rst
Normal file
@@ -0,0 +1,69 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
================================
|
||||
Linux I2C slave testunit backend
|
||||
================================
|
||||
|
||||
by Wolfram Sang <wsa@sang-engineering.com> in 2020
|
||||
|
||||
This backend can be used to trigger test cases for I2C bus masters which
|
||||
require a remote device with certain capabilities (and which are usually not so
|
||||
easy to obtain). Examples include multi-master testing, and SMBus Host Notify
|
||||
testing. For some tests, the I2C slave controller must be able to switch
|
||||
between master and slave mode because it needs to send data, too.
|
||||
|
||||
Note that this is a device for testing and debugging. It should not be enabled
|
||||
in a production build. And while there is some versioning and we try hard to
|
||||
keep backward compatibility, there is no stable ABI guaranteed!
|
||||
|
||||
Instantiating the device is regular. Example for bus 0, address 0x30:
|
||||
|
||||
# echo "slave-testunit 0x1030" > /sys/bus/i2c/devices/i2c-0/new_device
|
||||
|
||||
After that, you will have a write-only device listening. Reads will just return
|
||||
an 8-bit version number of the testunit. When writing, the device consists of 4
|
||||
8-bit registers and all must be written to start a testcase, i.e. you must
|
||||
always write 4 bytes to the device. The registers are:
|
||||
|
||||
0x00 CMD - which test to trigger
|
||||
0x01 DATAL - configuration byte 1 for the test
|
||||
0x02 DATAH - configuration byte 2 for the test
|
||||
0x03 DELAY - delay in n * 10ms until test is started
|
||||
|
||||
Using 'i2cset' from the i2c-tools package, the generic command looks like:
|
||||
|
||||
# i2cset -y <bus_num> <testunit_address> <CMD> <DATAL> <DATAH> <DELAY> i
|
||||
|
||||
DELAY is a generic parameter which will delay the execution of the test in CMD.
|
||||
While a command is running (including the delay), new commands will not be
|
||||
acknowledged. You need to wait until the old one is completed.
|
||||
|
||||
The commands are described in the following section. An invalid command will
|
||||
result in the transfer not being acknowledged.
|
||||
|
||||
Commands
|
||||
--------
|
||||
|
||||
0x00 NOOP (reserved for future use)
|
||||
|
||||
0x01 READ_BYTES (also needs master mode)
|
||||
DATAL - address to read data from (lower 7 bits, highest bit currently unused)
|
||||
DATAH - number of bytes to read
|
||||
|
||||
This is useful to test if your bus master driver is handling multi-master
|
||||
correctly. You can trigger the testunit to read bytes from another device on
|
||||
the bus. If the bus master under test also wants to access the bus at the same
|
||||
time, the bus will be busy. Example to read 128 bytes from device 0x50 after
|
||||
50ms of delay:
|
||||
|
||||
# i2cset -y 0 0x30 0x01 0x50 0x80 0x05 i
|
||||
|
||||
0x02 SMBUS_HOST_NOTIFY (also needs master mode)
|
||||
DATAL - low byte of the status word to send
|
||||
DATAH - high byte of the status word to send
|
||||
|
||||
This test will send an SMBUS_HOST_NOTIFY message to the host. Note that the
|
||||
status word is currently ignored in the Linux Kernel. Example to send a
|
||||
notification after 10ms:
|
||||
|
||||
# i2cset -y 0 0x30 0x02 0x42 0x64 0x01 i
|
@@ -11166,6 +11166,12 @@ W: http://www.melfas.com
|
||||
F: Documentation/devicetree/bindings/input/touchscreen/melfas_mip4.txt
|
||||
F: drivers/input/touchscreen/melfas_mip4.c
|
||||
|
||||
MELLANOX BLUEFIELD I2C DRIVER
|
||||
M: Khalil Blaiech <kblaiech@mellanox.com>
|
||||
L: linux-i2c@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/i2c/busses/i2c-mlxbf.c
|
||||
|
||||
MELLANOX ETHERNET DRIVER (mlx4_en)
|
||||
M: Tariq Toukan <tariqt@nvidia.com>
|
||||
L: netdev@vger.kernel.org
|
||||
|
@@ -4010,10 +4010,10 @@ static int rbd_try_lock(struct rbd_device *rbd_dev)
|
||||
rbd_warn(rbd_dev, "breaking header lock owned by %s%llu",
|
||||
ENTITY_NAME(lockers[0].id.name));
|
||||
|
||||
ret = ceph_monc_blacklist_add(&client->monc,
|
||||
ret = ceph_monc_blocklist_add(&client->monc,
|
||||
&lockers[0].info.addr);
|
||||
if (ret) {
|
||||
rbd_warn(rbd_dev, "blacklist of %s%llu failed: %d",
|
||||
rbd_warn(rbd_dev, "blocklist of %s%llu failed: %d",
|
||||
ENTITY_NAME(lockers[0].id.name), ret);
|
||||
goto out;
|
||||
}
|
||||
@@ -4077,7 +4077,7 @@ static int rbd_try_acquire_lock(struct rbd_device *rbd_dev)
|
||||
ret = rbd_try_lock(rbd_dev);
|
||||
if (ret < 0) {
|
||||
rbd_warn(rbd_dev, "failed to lock header: %d", ret);
|
||||
if (ret == -EBLACKLISTED)
|
||||
if (ret == -EBLOCKLISTED)
|
||||
goto out;
|
||||
|
||||
ret = 1; /* request lock anyway */
|
||||
@@ -4613,7 +4613,7 @@ static void rbd_reregister_watch(struct work_struct *work)
|
||||
ret = __rbd_register_watch(rbd_dev);
|
||||
if (ret) {
|
||||
rbd_warn(rbd_dev, "failed to reregister watch: %d", ret);
|
||||
if (ret != -EBLACKLISTED && ret != -ENOENT) {
|
||||
if (ret != -EBLOCKLISTED && ret != -ENOENT) {
|
||||
queue_delayed_work(rbd_dev->task_wq,
|
||||
&rbd_dev->watch_dwork,
|
||||
RBD_RETRY_DELAY);
|
||||
|
@@ -21,7 +21,7 @@ EXPORT_SYMBOL_GPL(dmi_kobj);
|
||||
/*
|
||||
* DMI stands for "Desktop Management Interface". It is part
|
||||
* of and an antecedent to, SMBIOS, which stands for System
|
||||
* Management BIOS. See further: http://www.dmtf.org/standards
|
||||
* Management BIOS. See further: https://www.dmtf.org/standards
|
||||
*/
|
||||
static const char dmi_empty_string[] = "";
|
||||
|
||||
|
@@ -101,7 +101,6 @@ source "drivers/i2c/busses/Kconfig"
|
||||
config I2C_STUB
|
||||
tristate "I2C/SMBus Test Stub"
|
||||
depends on m
|
||||
default 'n'
|
||||
help
|
||||
This module may be useful to developers of SMBus client drivers,
|
||||
especially for certain kinds of sensor chips.
|
||||
@@ -126,6 +125,14 @@ config I2C_SLAVE_EEPROM
|
||||
This backend makes Linux behave like an I2C EEPROM. Please read
|
||||
Documentation/i2c/slave-eeprom-backend.rst for further details.
|
||||
|
||||
config I2C_SLAVE_TESTUNIT
|
||||
tristate "I2C eeprom testunit driver"
|
||||
help
|
||||
This backend can be used to trigger test cases for I2C bus masters
|
||||
which require a remote device with certain capabilities, e.g.
|
||||
multi-master, SMBus Host Notify, etc. Please read
|
||||
Documentation/i2c/slave-testunit-backend.rst for further details.
|
||||
|
||||
endif
|
||||
|
||||
config I2C_DEBUG_CORE
|
||||
|
@@ -16,5 +16,6 @@ obj-$(CONFIG_I2C_MUX) += i2c-mux.o
|
||||
obj-y += algos/ busses/ muxes/
|
||||
obj-$(CONFIG_I2C_STUB) += i2c-stub.o
|
||||
obj-$(CONFIG_I2C_SLAVE_EEPROM) += i2c-slave-eeprom.o
|
||||
obj-$(CONFIG_I2C_SLAVE_TESTUNIT) += i2c-slave-testunit.o
|
||||
|
||||
ccflags-$(CONFIG_I2C_DEBUG_CORE) := -DDEBUG
|
||||
|
@@ -147,6 +147,7 @@ config I2C_I801
|
||||
Tiger Lake (PCH)
|
||||
Jasper Lake (SOC)
|
||||
Emmitsburg (PCH)
|
||||
Alder Lake (PCH)
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-i801.
|
||||
@@ -730,6 +731,19 @@ config I2C_LPC2K
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-lpc2k.
|
||||
|
||||
config I2C_MLXBF
|
||||
tristate "Mellanox BlueField I2C controller"
|
||||
depends on ARM64
|
||||
help
|
||||
Enabling this option will add I2C SMBus support for Mellanox BlueField
|
||||
system.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called i2c-mlxbf.
|
||||
|
||||
This driver implements an I2C SMBus host controller and enables both
|
||||
master and slave functions.
|
||||
|
||||
config I2C_MESON
|
||||
tristate "Amlogic Meson I2C controller"
|
||||
depends on ARCH_MESON || COMPILE_TEST
|
||||
@@ -840,7 +854,6 @@ config I2C_PASEMI
|
||||
config I2C_PCA_PLATFORM
|
||||
tristate "PCA9564/PCA9665 as platform device"
|
||||
select I2C_ALGOPCA
|
||||
default n
|
||||
help
|
||||
This driver supports a memory mapped Philips PCA9564/PCA9665
|
||||
parallel bus to I2C bus controller.
|
||||
@@ -1026,6 +1039,7 @@ config I2C_STM32F7
|
||||
tristate "STMicroelectronics STM32F7 I2C support"
|
||||
depends on ARCH_STM32 || COMPILE_TEST
|
||||
select I2C_SLAVE
|
||||
select I2C_SMBUS
|
||||
help
|
||||
Enable this option to add support for STM32 I2C controller embedded
|
||||
in STM32F7 SoCs.
|
||||
@@ -1181,6 +1195,8 @@ config I2C_RCAR
|
||||
tristate "Renesas R-Car I2C Controller"
|
||||
depends on ARCH_RENESAS || COMPILE_TEST
|
||||
select I2C_SLAVE
|
||||
select I2C_SMBUS
|
||||
select RESET_CONTROLLER if ARCH_RCAR_GEN3
|
||||
help
|
||||
If you say yes to this option, support will be included for the
|
||||
R-Car I2C controller.
|
||||
@@ -1240,7 +1256,6 @@ config I2C_TAOS_EVM
|
||||
depends on TTY
|
||||
select SERIO
|
||||
select SERIO_SERPORT
|
||||
default n
|
||||
help
|
||||
This supports TAOS evaluation modules on serial port. In order to
|
||||
use this driver, you will need the inputattach tool, which is part
|
||||
@@ -1324,7 +1339,6 @@ config I2C_PCA_ISA
|
||||
tristate "PCA9564/PCA9665 on an ISA bus"
|
||||
depends on ISA
|
||||
select I2C_ALGOPCA
|
||||
default n
|
||||
help
|
||||
This driver supports ISA boards using the Philips PCA9564/PCA9665
|
||||
parallel bus to I2C bus controller.
|
||||
|
@@ -140,6 +140,7 @@ obj-$(CONFIG_I2C_BRCMSTB) += i2c-brcmstb.o
|
||||
obj-$(CONFIG_I2C_CROS_EC_TUNNEL) += i2c-cros-ec-tunnel.o
|
||||
obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o
|
||||
obj-$(CONFIG_I2C_ICY) += i2c-icy.o
|
||||
obj-$(CONFIG_I2C_MLXBF) += i2c-mlxbf.o
|
||||
obj-$(CONFIG_I2C_MLXCPLD) += i2c-mlxcpld.o
|
||||
obj-$(CONFIG_I2C_OPAL) += i2c-opal.o
|
||||
obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o
|
||||
|
@@ -155,7 +155,7 @@ static int i2c_amd_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
||||
struct amd_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
|
||||
int i;
|
||||
struct i2c_msg *pmsg;
|
||||
int err;
|
||||
int err = 0;
|
||||
|
||||
/* the adapter might have been deleted while waiting for the bus lock */
|
||||
if (unlikely(!i2c_dev->common.mp2_dev))
|
||||
|
@@ -421,11 +421,9 @@ static int bcm2835_i2c_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(i2c_dev->regs);
|
||||
|
||||
mclk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(mclk)) {
|
||||
if (PTR_ERR(mclk) != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "Could not get clock\n");
|
||||
return PTR_ERR(mclk);
|
||||
}
|
||||
if (IS_ERR(mclk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(mclk),
|
||||
"Could not get clock\n");
|
||||
|
||||
i2c_dev->bus_clk = bcm2835_i2c_register_div(&pdev->dev, mclk, i2c_dev);
|
||||
|
||||
|
@@ -332,21 +332,15 @@ static int efm32_i2c_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "failed to determine base address\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
ddata->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
||||
if (IS_ERR(ddata->base))
|
||||
return PTR_ERR(ddata->base);
|
||||
|
||||
if (resource_size(res) < 0x42) {
|
||||
dev_err(&pdev->dev, "memory resource too small\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ddata->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(ddata->base))
|
||||
return PTR_ERR(ddata->base);
|
||||
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret <= 0) {
|
||||
if (!ret)
|
||||
|
@@ -71,6 +71,7 @@
|
||||
* Tiger Lake-H (PCH) 0x43a3 32 hard yes yes yes
|
||||
* Jasper Lake (SOC) 0x4da3 32 hard yes yes yes
|
||||
* Comet Lake-V (PCH) 0xa3a3 32 hard yes yes yes
|
||||
* Alder Lake-S (PCH) 0x7aa3 32 hard yes yes yes
|
||||
*
|
||||
* Features supported by this driver:
|
||||
* Software PEC no
|
||||
@@ -228,6 +229,7 @@
|
||||
#define PCI_DEVICE_ID_INTEL_ELKHART_LAKE_SMBUS 0x4b23
|
||||
#define PCI_DEVICE_ID_INTEL_JASPER_LAKE_SMBUS 0x4da3
|
||||
#define PCI_DEVICE_ID_INTEL_BROXTON_SMBUS 0x5ad4
|
||||
#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_S_SMBUS 0x7aa3
|
||||
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22
|
||||
#define PCI_DEVICE_ID_INTEL_WILDCATPOINT_SMBUS 0x8ca2
|
||||
#define PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS 0x8d22
|
||||
@@ -1081,6 +1083,7 @@ static const struct pci_device_id i801_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TIGERLAKE_LP_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TIGERLAKE_H_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_JASPER_LAKE_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ALDER_LAKE_S_SMBUS) },
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
@@ -1274,6 +1277,7 @@ static const struct {
|
||||
/*
|
||||
* Additional individual entries were added after verification.
|
||||
*/
|
||||
{ "Latitude 5480", 0x29 },
|
||||
{ "Vostro V131", 0x1d },
|
||||
};
|
||||
|
||||
@@ -1767,6 +1771,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
case PCI_DEVICE_ID_INTEL_TIGERLAKE_H_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_JASPER_LAKE_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_EBG_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_ALDER_LAKE_S_SMBUS:
|
||||
priv->features |= FEATURE_BLOCK_PROC;
|
||||
priv->features |= FEATURE_I2C_BLOCK_READ;
|
||||
priv->features |= FEATURE_IRQ;
|
||||
|
@@ -1159,11 +1159,9 @@ static int i2c_imx_probe(struct platform_device *pdev)
|
||||
|
||||
/* Get I2C clock */
|
||||
i2c_imx->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(i2c_imx->clk)) {
|
||||
if (PTR_ERR(i2c_imx->clk) != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "can't get I2C clock\n");
|
||||
return PTR_ERR(i2c_imx->clk);
|
||||
}
|
||||
if (IS_ERR(i2c_imx->clk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(i2c_imx->clk),
|
||||
"can't get I2C clock\n");
|
||||
|
||||
ret = clk_prepare_enable(i2c_imx->clk);
|
||||
if (ret) {
|
||||
@@ -1171,14 +1169,6 @@ static int i2c_imx_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Request IRQ */
|
||||
ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, IRQF_SHARED,
|
||||
pdev->name, i2c_imx);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "can't claim irq %d\n", irq);
|
||||
goto clk_disable;
|
||||
}
|
||||
|
||||
/* Init queue */
|
||||
init_waitqueue_head(&i2c_imx->queue);
|
||||
|
||||
@@ -1197,6 +1187,14 @@ static int i2c_imx_probe(struct platform_device *pdev)
|
||||
if (ret < 0)
|
||||
goto rpm_disable;
|
||||
|
||||
/* Request IRQ */
|
||||
ret = request_threaded_irq(irq, i2c_imx_isr, NULL, IRQF_SHARED,
|
||||
pdev->name, i2c_imx);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "can't claim irq %d\n", irq);
|
||||
goto rpm_disable;
|
||||
}
|
||||
|
||||
/* Set up clock divider */
|
||||
i2c_imx->bitrate = I2C_MAX_STANDARD_MODE_FREQ;
|
||||
ret = of_property_read_u32(pdev->dev.of_node,
|
||||
@@ -1239,13 +1237,12 @@ static int i2c_imx_probe(struct platform_device *pdev)
|
||||
|
||||
clk_notifier_unregister:
|
||||
clk_notifier_unregister(i2c_imx->clk, &i2c_imx->clk_change_nb);
|
||||
free_irq(irq, i2c_imx);
|
||||
rpm_disable:
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||
|
||||
clk_disable:
|
||||
clk_disable_unprepare(i2c_imx->clk);
|
||||
return ret;
|
||||
}
|
||||
@@ -1253,7 +1250,7 @@ clk_disable:
|
||||
static int i2c_imx_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct imx_i2c_struct *i2c_imx = platform_get_drvdata(pdev);
|
||||
int ret;
|
||||
int irq, ret;
|
||||
|
||||
ret = pm_runtime_get_sync(&pdev->dev);
|
||||
if (ret < 0)
|
||||
@@ -1273,6 +1270,9 @@ static int i2c_imx_remove(struct platform_device *pdev)
|
||||
imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2SR);
|
||||
|
||||
clk_notifier_unregister(i2c_imx->clk, &i2c_imx->clk_change_nb);
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq >= 0)
|
||||
free_irq(irq, i2c_imx);
|
||||
clk_disable_unprepare(i2c_imx->clk);
|
||||
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
|
@@ -77,6 +77,7 @@
|
||||
#define PCI_DEVICE_ID_INTEL_S1200_SMT1 0x0c5a
|
||||
#define PCI_DEVICE_ID_INTEL_CDF_SMT 0x18ac
|
||||
#define PCI_DEVICE_ID_INTEL_DNV_SMT 0x19ac
|
||||
#define PCI_DEVICE_ID_INTEL_EBG_SMT 0x1bff
|
||||
#define PCI_DEVICE_ID_INTEL_AVOTON_SMT 0x1f15
|
||||
|
||||
#define ISMT_DESC_ENTRIES 2 /* number of descriptor entries */
|
||||
@@ -176,14 +177,12 @@ struct ismt_priv {
|
||||
u8 buffer[I2C_SMBUS_BLOCK_MAX + 16]; /* temp R/W data buffer */
|
||||
};
|
||||
|
||||
/**
|
||||
* ismt_ids - PCI device IDs supported by this driver
|
||||
*/
|
||||
static const struct pci_device_id ismt_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_S1200_SMT0) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_S1200_SMT1) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CDF_SMT) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_DNV_SMT) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EBG_SMT) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_AVOTON_SMT) },
|
||||
{ 0, }
|
||||
};
|
||||
@@ -197,6 +196,8 @@ MODULE_PARM_DESC(bus_speed, "Bus Speed in kHz (0 = BIOS default)");
|
||||
|
||||
/**
|
||||
* __ismt_desc_dump() - dump the contents of a specific descriptor
|
||||
* @dev: the iSMT device
|
||||
* @desc: the iSMT hardware descriptor
|
||||
*/
|
||||
static void __ismt_desc_dump(struct device *dev, const struct ismt_desc *desc)
|
||||
{
|
||||
@@ -628,11 +629,6 @@ static u32 ismt_func(struct i2c_adapter *adap)
|
||||
I2C_FUNC_SMBUS_PEC;
|
||||
}
|
||||
|
||||
/**
|
||||
* smbus_algorithm - the adapter algorithm and supported functionality
|
||||
* @smbus_xfer: the adapter algorithm
|
||||
* @functionality: functionality supported by the adapter
|
||||
*/
|
||||
static const struct i2c_algorithm smbus_algorithm = {
|
||||
.smbus_xfer = ismt_access,
|
||||
.functionality = ismt_func,
|
||||
|
@@ -752,6 +752,7 @@ static const struct ingenic_i2c_config x1000_i2c_config = {
|
||||
};
|
||||
|
||||
static const struct of_device_id jz4780_i2c_of_matches[] = {
|
||||
{ .compatible = "ingenic,jz4770-i2c", .data = &jz4780_i2c_config },
|
||||
{ .compatible = "ingenic,jz4780-i2c", .data = &jz4780_i2c_config },
|
||||
{ .compatible = "ingenic,x1000-i2c", .data = &x1000_i2c_config },
|
||||
{ /* sentinel */ }
|
||||
@@ -856,7 +857,7 @@ static struct platform_driver jz4780_i2c_driver = {
|
||||
.remove = jz4780_i2c_remove,
|
||||
.driver = {
|
||||
.name = "jz4780-i2c",
|
||||
.of_match_table = of_match_ptr(jz4780_i2c_of_matches),
|
||||
.of_match_table = jz4780_i2c_of_matches,
|
||||
},
|
||||
};
|
||||
|
||||
|
2506
drivers/i2c/busses/i2c-mlxbf.c
Normal file
2506
drivers/i2c/busses/i2c-mlxbf.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -496,11 +496,10 @@ static irqreturn_t
|
||||
mv64xxx_i2c_intr(int irq, void *dev_id)
|
||||
{
|
||||
struct mv64xxx_i2c_data *drv_data = dev_id;
|
||||
unsigned long flags;
|
||||
u32 status;
|
||||
irqreturn_t rc = IRQ_NONE;
|
||||
|
||||
spin_lock_irqsave(&drv_data->lock, flags);
|
||||
spin_lock(&drv_data->lock);
|
||||
|
||||
if (drv_data->offload_enabled)
|
||||
rc = mv64xxx_i2c_intr_offload(drv_data);
|
||||
@@ -517,7 +516,7 @@ mv64xxx_i2c_intr(int irq, void *dev_id)
|
||||
|
||||
rc = IRQ_HANDLED;
|
||||
}
|
||||
spin_unlock_irqrestore(&drv_data->lock, flags);
|
||||
spin_unlock(&drv_data->lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
@@ -125,8 +125,7 @@ static int gpu_i2c_read(struct gpu_i2c_dev *i2cd, u8 *data, u16 len)
|
||||
put_unaligned_be16(val, data);
|
||||
break;
|
||||
case 3:
|
||||
put_unaligned_be16(val >> 8, data);
|
||||
data[2] = val;
|
||||
put_unaligned_be24(val, data);
|
||||
break;
|
||||
case 4:
|
||||
put_unaligned_be32(val, data);
|
||||
|
@@ -165,10 +165,9 @@ static irqreturn_t owl_i2c_interrupt(int irq, void *_dev)
|
||||
{
|
||||
struct owl_i2c_dev *i2c_dev = _dev;
|
||||
struct i2c_msg *msg = i2c_dev->msg;
|
||||
unsigned long flags;
|
||||
unsigned int stat, fifostat;
|
||||
|
||||
spin_lock_irqsave(&i2c_dev->lock, flags);
|
||||
spin_lock(&i2c_dev->lock);
|
||||
|
||||
i2c_dev->err = 0;
|
||||
|
||||
@@ -214,7 +213,7 @@ stop:
|
||||
OWL_I2C_STAT_IRQP, true);
|
||||
|
||||
complete_all(&i2c_dev->msg_complete);
|
||||
spin_unlock_irqrestore(&i2c_dev->lock, flags);
|
||||
spin_unlock(&i2c_dev->lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
@@ -210,9 +210,8 @@ static irqreturn_t geni_i2c_irq(int irq, void *dev)
|
||||
u32 dma;
|
||||
u32 val;
|
||||
struct i2c_msg *cur;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&gi2c->lock, flags);
|
||||
spin_lock(&gi2c->lock);
|
||||
m_stat = readl_relaxed(base + SE_GENI_M_IRQ_STATUS);
|
||||
rx_st = readl_relaxed(base + SE_GENI_RX_FIFO_STATUS);
|
||||
dm_tx_st = readl_relaxed(base + SE_DMA_TX_IRQ_STAT);
|
||||
@@ -294,7 +293,7 @@ static irqreturn_t geni_i2c_irq(int irq, void *dev)
|
||||
dm_rx_st & RX_DMA_DONE || dm_rx_st & RX_RESET_DONE)
|
||||
complete(&gi2c->done);
|
||||
|
||||
spin_unlock_irqrestore(&gi2c->lock, flags);
|
||||
spin_unlock(&gi2c->lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
@@ -19,7 +19,9 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-smbus.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
@@ -105,10 +107,11 @@
|
||||
#define ID_ARBLOST (1 << 3)
|
||||
#define ID_NACK (1 << 4)
|
||||
/* persistent flags */
|
||||
#define ID_P_HOST_NOTIFY BIT(28)
|
||||
#define ID_P_REP_AFTER_RD BIT(29)
|
||||
#define ID_P_NO_RXDMA BIT(30) /* HW forbids RXDMA sometimes */
|
||||
#define ID_P_PM_BLOCKED BIT(31)
|
||||
#define ID_P_MASK GENMASK(31, 29)
|
||||
#define ID_P_MASK GENMASK(31, 28)
|
||||
|
||||
enum rcar_i2c_type {
|
||||
I2C_RCAR_GEN1,
|
||||
@@ -140,14 +143,13 @@ struct rcar_i2c_priv {
|
||||
|
||||
struct reset_control *rstc;
|
||||
int irq;
|
||||
|
||||
struct i2c_client *host_notify_client;
|
||||
};
|
||||
|
||||
#define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent)
|
||||
#define rcar_i2c_is_recv(p) ((p)->msg->flags & I2C_M_RD)
|
||||
|
||||
#define LOOP_TIMEOUT 1024
|
||||
|
||||
|
||||
static void rcar_i2c_write(struct rcar_i2c_priv *priv, int reg, u32 val)
|
||||
{
|
||||
writel(val, priv->io + reg);
|
||||
@@ -221,18 +223,18 @@ static void rcar_i2c_init(struct rcar_i2c_priv *priv)
|
||||
|
||||
static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
for (i = 0; i < LOOP_TIMEOUT; i++) {
|
||||
/* make sure that bus is not busy */
|
||||
if (!(rcar_i2c_read(priv, ICMCR) & FSDA))
|
||||
return 0;
|
||||
udelay(1);
|
||||
ret = readl_poll_timeout(priv->io + ICMCR, val, !(val & FSDA), 10,
|
||||
priv->adap.timeout);
|
||||
if (ret) {
|
||||
/* Waiting did not help, try to recover */
|
||||
priv->recovery_icmcr = MDBS | OBPC | FSDA | FSCL;
|
||||
ret = i2c_recover_bus(&priv->adap);
|
||||
}
|
||||
|
||||
/* Waiting did not help, try to recover */
|
||||
priv->recovery_icmcr = MDBS | OBPC | FSDA | FSCL;
|
||||
return i2c_recover_bus(&priv->adap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv)
|
||||
@@ -760,20 +762,14 @@ static void rcar_i2c_release_dma(struct rcar_i2c_priv *priv)
|
||||
/* I2C is a special case, we need to poll the status of a reset */
|
||||
static int rcar_i2c_do_reset(struct rcar_i2c_priv *priv)
|
||||
{
|
||||
int i, ret;
|
||||
int ret;
|
||||
|
||||
ret = reset_control_reset(priv->rstc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < LOOP_TIMEOUT; i++) {
|
||||
ret = reset_control_status(priv->rstc);
|
||||
if (ret == 0)
|
||||
return 0;
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
return -ETIMEDOUT;
|
||||
return read_poll_timeout_atomic(reset_control_status, ret, ret == 0, 1,
|
||||
100, false, priv->rstc);
|
||||
}
|
||||
|
||||
static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
|
||||
@@ -884,14 +880,21 @@ static int rcar_unreg_slave(struct i2c_client *slave)
|
||||
|
||||
static u32 rcar_i2c_func(struct i2c_adapter *adap)
|
||||
{
|
||||
struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
|
||||
|
||||
/*
|
||||
* This HW can't do:
|
||||
* I2C_SMBUS_QUICK (setting FSB during START didn't work)
|
||||
* I2C_M_NOSTART (automatically sends address after START)
|
||||
* I2C_M_IGNORE_NAK (automatically sends STOP after NAK)
|
||||
*/
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SLAVE |
|
||||
(I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
|
||||
u32 func = I2C_FUNC_I2C | I2C_FUNC_SLAVE |
|
||||
(I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
|
||||
|
||||
if (priv->flags & ID_P_HOST_NOTIFY)
|
||||
func |= I2C_FUNC_SMBUS_HOST_NOTIFY;
|
||||
|
||||
return func;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm rcar_i2c_algo = {
|
||||
@@ -991,6 +994,8 @@ static int rcar_i2c_probe(struct platform_device *pdev)
|
||||
else
|
||||
pm_runtime_put(dev);
|
||||
|
||||
if (of_property_read_bool(dev->of_node, "smbus"))
|
||||
priv->flags |= ID_P_HOST_NOTIFY;
|
||||
|
||||
priv->irq = platform_get_irq(pdev, 0);
|
||||
ret = devm_request_irq(dev, priv->irq, rcar_i2c_irq, 0, dev_name(dev), priv);
|
||||
@@ -1005,10 +1010,20 @@ static int rcar_i2c_probe(struct platform_device *pdev)
|
||||
if (ret < 0)
|
||||
goto out_pm_disable;
|
||||
|
||||
if (priv->flags & ID_P_HOST_NOTIFY) {
|
||||
priv->host_notify_client = i2c_new_slave_host_notify_device(adap);
|
||||
if (IS_ERR(priv->host_notify_client)) {
|
||||
ret = PTR_ERR(priv->host_notify_client);
|
||||
goto out_del_device;
|
||||
}
|
||||
}
|
||||
|
||||
dev_info(dev, "probed\n");
|
||||
|
||||
return 0;
|
||||
|
||||
out_del_device:
|
||||
i2c_del_adapter(&priv->adap);
|
||||
out_pm_put:
|
||||
pm_runtime_put(dev);
|
||||
out_pm_disable:
|
||||
@@ -1021,6 +1036,8 @@ static int rcar_i2c_remove(struct platform_device *pdev)
|
||||
struct rcar_i2c_priv *priv = platform_get_drvdata(pdev);
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
if (priv->host_notify_client)
|
||||
i2c_free_slave_host_notify_device(priv->host_notify_client);
|
||||
i2c_del_adapter(&priv->adap);
|
||||
rcar_i2c_release_dma(priv);
|
||||
if (priv->flags & ID_P_PM_BLOCKED)
|
||||
|
@@ -1312,18 +1312,13 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
|
||||
i2c->pclk = devm_clk_get(&pdev->dev, "pclk");
|
||||
}
|
||||
|
||||
if (IS_ERR(i2c->clk)) {
|
||||
ret = PTR_ERR(i2c->clk);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "Can't get bus clk: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
if (IS_ERR(i2c->pclk)) {
|
||||
ret = PTR_ERR(i2c->pclk);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "Can't get periph clk: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
if (IS_ERR(i2c->clk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(i2c->clk),
|
||||
"Can't get bus clk\n");
|
||||
|
||||
if (IS_ERR(i2c->pclk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(i2c->pclk),
|
||||
"Can't get periph clk\n");
|
||||
|
||||
ret = clk_prepare(i2c->clk);
|
||||
if (ret < 0) {
|
||||
|
@@ -26,8 +26,9 @@ struct stm32_i2c_dma *stm32_i2c_dma_request(struct device *dev,
|
||||
dma->chan_tx = dma_request_chan(dev, "tx");
|
||||
if (IS_ERR(dma->chan_tx)) {
|
||||
ret = PTR_ERR(dma->chan_tx);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "can't request DMA tx channel\n");
|
||||
if (ret != -ENODEV)
|
||||
ret = dev_err_probe(dev, ret,
|
||||
"can't request DMA tx channel\n");
|
||||
goto fail_al;
|
||||
}
|
||||
|
||||
@@ -46,8 +47,9 @@ struct stm32_i2c_dma *stm32_i2c_dma_request(struct device *dev,
|
||||
dma->chan_rx = dma_request_chan(dev, "rx");
|
||||
if (IS_ERR(dma->chan_rx)) {
|
||||
ret = PTR_ERR(dma->chan_rx);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "can't request DMA rx channel\n");
|
||||
if (ret != -ENODEV)
|
||||
ret = dev_err_probe(dev, ret,
|
||||
"can't request DMA rx channel\n");
|
||||
|
||||
goto fail_tx;
|
||||
}
|
||||
@@ -76,8 +78,6 @@ fail_tx:
|
||||
dma_release_channel(dma->chan_tx);
|
||||
fail_al:
|
||||
devm_kfree(dev, dma);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_info(dev, "can't use DMA\n");
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
@@ -797,10 +797,8 @@ static int stm32f4_i2c_probe(struct platform_device *pdev)
|
||||
|
||||
rst = devm_reset_control_get_exclusive(&pdev->dev, NULL);
|
||||
if (IS_ERR(rst)) {
|
||||
ret = PTR_ERR(rst);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "Error: Missing reset ctrl\n");
|
||||
|
||||
ret = dev_err_probe(&pdev->dev, PTR_ERR(rst),
|
||||
"Error: Missing reset ctrl\n");
|
||||
goto clk_free;
|
||||
}
|
||||
reset_control_assert(rst);
|
||||
|
@@ -18,6 +18,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-smbus.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
@@ -50,6 +51,7 @@
|
||||
|
||||
/* STM32F7 I2C control 1 */
|
||||
#define STM32F7_I2C_CR1_PECEN BIT(23)
|
||||
#define STM32F7_I2C_CR1_SMBHEN BIT(20)
|
||||
#define STM32F7_I2C_CR1_WUPEN BIT(18)
|
||||
#define STM32F7_I2C_CR1_SBC BIT(16)
|
||||
#define STM32F7_I2C_CR1_RXDMAEN BIT(15)
|
||||
@@ -150,7 +152,12 @@
|
||||
|
||||
#define STM32F7_I2C_MAX_LEN 0xff
|
||||
#define STM32F7_I2C_DMA_LEN_MIN 0x16
|
||||
#define STM32F7_I2C_MAX_SLAVE 0x2
|
||||
enum {
|
||||
STM32F7_SLAVE_HOSTNOTIFY,
|
||||
STM32F7_SLAVE_7_10_BITS_ADDR,
|
||||
STM32F7_SLAVE_7_BITS_ADDR,
|
||||
STM32F7_I2C_MAX_SLAVE
|
||||
};
|
||||
|
||||
#define STM32F7_I2C_DNF_DEFAULT 0
|
||||
#define STM32F7_I2C_DNF_MAX 16
|
||||
@@ -301,6 +308,8 @@ struct stm32f7_i2c_msg {
|
||||
* @fmp_creg: register address for clearing Fast Mode Plus bits
|
||||
* @fmp_mask: mask for Fast Mode Plus bits in set register
|
||||
* @wakeup_src: boolean to know if the device is a wakeup source
|
||||
* @smbus_mode: states that the controller is configured in SMBus mode
|
||||
* @host_notify_client: SMBus host-notify client
|
||||
*/
|
||||
struct stm32f7_i2c_dev {
|
||||
struct i2c_adapter adap;
|
||||
@@ -327,6 +336,8 @@ struct stm32f7_i2c_dev {
|
||||
u32 fmp_creg;
|
||||
u32 fmp_mask;
|
||||
bool wakeup_src;
|
||||
bool smbus_mode;
|
||||
struct i2c_client *host_notify_client;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -1321,11 +1332,20 @@ static int stm32f7_i2c_get_free_slave_id(struct stm32f7_i2c_dev *i2c_dev,
|
||||
int i;
|
||||
|
||||
/*
|
||||
* slave[0] supports 7-bit and 10-bit slave address
|
||||
* slave[1] supports 7-bit slave address only
|
||||
* slave[STM32F7_SLAVE_HOSTNOTIFY] support only SMBus Host address (0x8)
|
||||
* slave[STM32F7_SLAVE_7_10_BITS_ADDR] supports 7-bit and 10-bit slave address
|
||||
* slave[STM32F7_SLAVE_7_BITS_ADDR] supports 7-bit slave address only
|
||||
*/
|
||||
for (i = STM32F7_I2C_MAX_SLAVE - 1; i >= 0; i--) {
|
||||
if (i == 1 && (slave->flags & I2C_CLIENT_TEN))
|
||||
if (i2c_dev->smbus_mode && (slave->addr == 0x08)) {
|
||||
if (i2c_dev->slave[STM32F7_SLAVE_HOSTNOTIFY])
|
||||
goto fail;
|
||||
*id = STM32F7_SLAVE_HOSTNOTIFY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = STM32F7_I2C_MAX_SLAVE - 1; i > STM32F7_SLAVE_HOSTNOTIFY; i--) {
|
||||
if ((i == STM32F7_SLAVE_7_BITS_ADDR) &&
|
||||
(slave->flags & I2C_CLIENT_TEN))
|
||||
continue;
|
||||
if (!i2c_dev->slave[i]) {
|
||||
*id = i;
|
||||
@@ -1333,6 +1353,7 @@ static int stm32f7_i2c_get_free_slave_id(struct stm32f7_i2c_dev *i2c_dev,
|
||||
}
|
||||
}
|
||||
|
||||
fail:
|
||||
dev_err(dev, "Slave 0x%x could not be registered\n", slave->addr);
|
||||
|
||||
return -EINVAL;
|
||||
@@ -1776,7 +1797,13 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave)
|
||||
if (!stm32f7_i2c_is_slave_registered(i2c_dev))
|
||||
stm32f7_i2c_enable_wakeup(i2c_dev, true);
|
||||
|
||||
if (id == 0) {
|
||||
switch (id) {
|
||||
case 0:
|
||||
/* Slave SMBus Host */
|
||||
i2c_dev->slave[id] = slave;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
/* Configure Own Address 1 */
|
||||
oar1 = readl_relaxed(i2c_dev->base + STM32F7_I2C_OAR1);
|
||||
oar1 &= ~STM32F7_I2C_OAR1_MASK;
|
||||
@@ -1789,7 +1816,9 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave)
|
||||
oar1 |= STM32F7_I2C_OAR1_OA1EN;
|
||||
i2c_dev->slave[id] = slave;
|
||||
writel_relaxed(oar1, i2c_dev->base + STM32F7_I2C_OAR1);
|
||||
} else if (id == 1) {
|
||||
break;
|
||||
|
||||
case 2:
|
||||
/* Configure Own Address 2 */
|
||||
oar2 = readl_relaxed(i2c_dev->base + STM32F7_I2C_OAR2);
|
||||
oar2 &= ~STM32F7_I2C_OAR2_MASK;
|
||||
@@ -1802,7 +1831,10 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave)
|
||||
oar2 |= STM32F7_I2C_OAR2_OA2EN;
|
||||
i2c_dev->slave[id] = slave;
|
||||
writel_relaxed(oar2, i2c_dev->base + STM32F7_I2C_OAR2);
|
||||
} else {
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(dev, "I2C slave id not supported\n");
|
||||
ret = -ENODEV;
|
||||
goto pm_free;
|
||||
}
|
||||
@@ -1843,10 +1875,10 @@ static int stm32f7_i2c_unreg_slave(struct i2c_client *slave)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (id == 0) {
|
||||
if (id == 1) {
|
||||
mask = STM32F7_I2C_OAR1_OA1EN;
|
||||
stm32f7_i2c_clr_bits(base + STM32F7_I2C_OAR1, mask);
|
||||
} else {
|
||||
} else if (id == 2) {
|
||||
mask = STM32F7_I2C_OAR2_OA2EN;
|
||||
stm32f7_i2c_clr_bits(base + STM32F7_I2C_OAR2, mask);
|
||||
}
|
||||
@@ -1911,14 +1943,51 @@ static int stm32f7_i2c_setup_fm_plus_bits(struct platform_device *pdev,
|
||||
&i2c_dev->fmp_mask);
|
||||
}
|
||||
|
||||
static int stm32f7_i2c_enable_smbus_host(struct stm32f7_i2c_dev *i2c_dev)
|
||||
{
|
||||
struct i2c_adapter *adap = &i2c_dev->adap;
|
||||
void __iomem *base = i2c_dev->base;
|
||||
struct i2c_client *client;
|
||||
|
||||
client = i2c_new_slave_host_notify_device(adap);
|
||||
if (IS_ERR(client))
|
||||
return PTR_ERR(client);
|
||||
|
||||
i2c_dev->host_notify_client = client;
|
||||
|
||||
/* Enable SMBus Host address */
|
||||
stm32f7_i2c_set_bits(base + STM32F7_I2C_CR1, STM32F7_I2C_CR1_SMBHEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stm32f7_i2c_disable_smbus_host(struct stm32f7_i2c_dev *i2c_dev)
|
||||
{
|
||||
void __iomem *base = i2c_dev->base;
|
||||
|
||||
if (i2c_dev->host_notify_client) {
|
||||
/* Disable SMBus Host address */
|
||||
stm32f7_i2c_clr_bits(base + STM32F7_I2C_CR1,
|
||||
STM32F7_I2C_CR1_SMBHEN);
|
||||
i2c_free_slave_host_notify_device(i2c_dev->host_notify_client);
|
||||
}
|
||||
}
|
||||
|
||||
static u32 stm32f7_i2c_func(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SLAVE |
|
||||
I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
|
||||
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
|
||||
I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
|
||||
I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_PEC |
|
||||
I2C_FUNC_SMBUS_I2C_BLOCK;
|
||||
struct stm32f7_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
|
||||
|
||||
u32 func = I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SLAVE |
|
||||
I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
|
||||
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
|
||||
I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
|
||||
I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_PEC |
|
||||
I2C_FUNC_SMBUS_I2C_BLOCK;
|
||||
|
||||
if (i2c_dev->smbus_mode)
|
||||
func |= I2C_FUNC_SMBUS_HOST_NOTIFY;
|
||||
|
||||
return func;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm stm32f7_i2c_algo = {
|
||||
@@ -1968,11 +2037,9 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
|
||||
"wakeup-source");
|
||||
|
||||
i2c_dev->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(i2c_dev->clk)) {
|
||||
if (PTR_ERR(i2c_dev->clk) != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "Failed to get controller clock\n");
|
||||
return PTR_ERR(i2c_dev->clk);
|
||||
}
|
||||
if (IS_ERR(i2c_dev->clk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(i2c_dev->clk),
|
||||
"Failed to get controller clock\n");
|
||||
|
||||
ret = clk_prepare_enable(i2c_dev->clk);
|
||||
if (ret) {
|
||||
@@ -1982,10 +2049,8 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
|
||||
|
||||
rst = devm_reset_control_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(rst)) {
|
||||
ret = PTR_ERR(rst);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "Error: Missing reset ctrl\n");
|
||||
|
||||
ret = dev_err_probe(&pdev->dev, PTR_ERR(rst),
|
||||
"Error: Missing reset ctrl\n");
|
||||
goto clk_free;
|
||||
}
|
||||
reset_control_assert(rst);
|
||||
@@ -2052,14 +2117,13 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
|
||||
i2c_dev->dma = stm32_i2c_dma_request(i2c_dev->dev, phy_addr,
|
||||
STM32F7_I2C_TXDR,
|
||||
STM32F7_I2C_RXDR);
|
||||
if (PTR_ERR(i2c_dev->dma) == -ENODEV)
|
||||
i2c_dev->dma = NULL;
|
||||
else if (IS_ERR(i2c_dev->dma)) {
|
||||
if (IS_ERR(i2c_dev->dma)) {
|
||||
ret = PTR_ERR(i2c_dev->dma);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to request dma error %i\n", ret);
|
||||
goto fmp_clear;
|
||||
/* DMA support is optional, only report other errors */
|
||||
if (ret != -ENODEV)
|
||||
goto fmp_clear;
|
||||
dev_dbg(i2c_dev->dev, "No DMA option: fallback using interrupts\n");
|
||||
i2c_dev->dma = NULL;
|
||||
}
|
||||
|
||||
if (i2c_dev->wakeup_src) {
|
||||
@@ -2084,10 +2148,22 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
|
||||
|
||||
stm32f7_i2c_hw_config(i2c_dev);
|
||||
|
||||
i2c_dev->smbus_mode = of_property_read_bool(pdev->dev.of_node, "smbus");
|
||||
|
||||
ret = i2c_add_adapter(adap);
|
||||
if (ret)
|
||||
goto pm_disable;
|
||||
|
||||
if (i2c_dev->smbus_mode) {
|
||||
ret = stm32f7_i2c_enable_smbus_host(i2c_dev);
|
||||
if (ret) {
|
||||
dev_err(i2c_dev->dev,
|
||||
"failed to enable SMBus Host-Notify protocol (%d)\n",
|
||||
ret);
|
||||
goto i2c_adapter_remove;
|
||||
}
|
||||
}
|
||||
|
||||
dev_info(i2c_dev->dev, "STM32F7 I2C-%d bus adapter\n", adap->nr);
|
||||
|
||||
pm_runtime_mark_last_busy(i2c_dev->dev);
|
||||
@@ -2095,6 +2171,9 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
|
||||
|
||||
return 0;
|
||||
|
||||
i2c_adapter_remove:
|
||||
i2c_del_adapter(adap);
|
||||
|
||||
pm_disable:
|
||||
pm_runtime_put_noidle(i2c_dev->dev);
|
||||
pm_runtime_disable(i2c_dev->dev);
|
||||
@@ -2126,6 +2205,8 @@ static int stm32f7_i2c_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct stm32f7_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
|
||||
|
||||
stm32f7_i2c_disable_smbus_host(i2c_dev);
|
||||
|
||||
i2c_del_adapter(&i2c_dev->adap);
|
||||
pm_runtime_get_sync(i2c_dev->dev);
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -46,34 +46,36 @@ enum xiic_endian {
|
||||
|
||||
/**
|
||||
* struct xiic_i2c - Internal representation of the XIIC I2C bus
|
||||
* @dev: Pointer to device structure
|
||||
* @base: Memory base of the HW registers
|
||||
* @wait: Wait queue for callers
|
||||
* @adap: Kernel adapter representation
|
||||
* @tx_msg: Messages from above to be sent
|
||||
* @lock: Mutual exclusion
|
||||
* @tx_pos: Current pos in TX message
|
||||
* @nmsgs: Number of messages in tx_msg
|
||||
* @state: See STATE_
|
||||
* @rx_msg: Current RX message
|
||||
* @rx_pos: Position within current RX message
|
||||
* @dev: Pointer to device structure
|
||||
* @base: Memory base of the HW registers
|
||||
* @wait: Wait queue for callers
|
||||
* @adap: Kernel adapter representation
|
||||
* @tx_msg: Messages from above to be sent
|
||||
* @lock: Mutual exclusion
|
||||
* @tx_pos: Current pos in TX message
|
||||
* @nmsgs: Number of messages in tx_msg
|
||||
* @rx_msg: Current RX message
|
||||
* @rx_pos: Position within current RX message
|
||||
* @endianness: big/little-endian byte order
|
||||
* @clk: Pointer to AXI4-lite input clock
|
||||
* @clk: Pointer to AXI4-lite input clock
|
||||
* @state: See STATE_
|
||||
* @singlemaster: Indicates bus is single master
|
||||
*/
|
||||
struct xiic_i2c {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
wait_queue_head_t wait;
|
||||
struct i2c_adapter adap;
|
||||
struct i2c_msg *tx_msg;
|
||||
struct mutex lock;
|
||||
unsigned int tx_pos;
|
||||
unsigned int nmsgs;
|
||||
enum xilinx_i2c_state state;
|
||||
struct i2c_msg *rx_msg;
|
||||
int rx_pos;
|
||||
enum xiic_endian endianness;
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
wait_queue_head_t wait;
|
||||
struct i2c_adapter adap;
|
||||
struct i2c_msg *tx_msg;
|
||||
struct mutex lock;
|
||||
unsigned int tx_pos;
|
||||
unsigned int nmsgs;
|
||||
struct i2c_msg *rx_msg;
|
||||
int rx_pos;
|
||||
enum xiic_endian endianness;
|
||||
struct clk *clk;
|
||||
enum xilinx_i2c_state state;
|
||||
bool singlemaster;
|
||||
};
|
||||
|
||||
|
||||
@@ -526,6 +528,15 @@ static int xiic_busy(struct xiic_i2c *i2c)
|
||||
if (i2c->tx_msg)
|
||||
return -EBUSY;
|
||||
|
||||
/* In single master mode bus can only be busy, when in use by this
|
||||
* driver. If the register indicates bus being busy for some reason we
|
||||
* should ignore it, since bus will never be released and i2c will be
|
||||
* stuck forever.
|
||||
*/
|
||||
if (i2c->singlemaster) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* for instance if previous transfer was terminated due to TX error
|
||||
* it might be that the bus is on it's way to become available
|
||||
* give it at most 3 ms to wake
|
||||
@@ -811,6 +822,9 @@ static int xiic_i2c_probe(struct platform_device *pdev)
|
||||
goto err_clk_dis;
|
||||
}
|
||||
|
||||
i2c->singlemaster =
|
||||
of_property_read_bool(pdev->dev.of_node, "single-master");
|
||||
|
||||
/*
|
||||
* Detect endianness
|
||||
* Try to reset the TX FIFO. Then check the EMPTY flag. If it is not
|
||||
|
175
drivers/i2c/i2c-slave-testunit.c
Normal file
175
drivers/i2c/i2c-slave-testunit.c
Normal file
@@ -0,0 +1,175 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* I2C slave mode testunit
|
||||
*
|
||||
* Copyright (C) 2020 by Wolfram Sang, Sang Engineering <wsa@sang-engineering.com>
|
||||
* Copyright (C) 2020 by Renesas Electronics Corporation
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/workqueue.h> /* FIXME: is system_long_wq the best choice? */
|
||||
|
||||
#define TU_CUR_VERSION 0x01
|
||||
|
||||
enum testunit_cmds {
|
||||
TU_CMD_READ_BYTES = 1, /* save 0 for ABORT, RESET or similar */
|
||||
TU_CMD_HOST_NOTIFY,
|
||||
TU_NUM_CMDS
|
||||
};
|
||||
|
||||
enum testunit_regs {
|
||||
TU_REG_CMD,
|
||||
TU_REG_DATAL,
|
||||
TU_REG_DATAH,
|
||||
TU_REG_DELAY,
|
||||
TU_NUM_REGS
|
||||
};
|
||||
|
||||
enum testunit_flags {
|
||||
TU_FLAG_IN_PROCESS,
|
||||
};
|
||||
|
||||
struct testunit_data {
|
||||
unsigned long flags;
|
||||
u8 regs[TU_NUM_REGS];
|
||||
u8 reg_idx;
|
||||
struct i2c_client *client;
|
||||
struct delayed_work worker;
|
||||
};
|
||||
|
||||
static void i2c_slave_testunit_work(struct work_struct *work)
|
||||
{
|
||||
struct testunit_data *tu = container_of(work, struct testunit_data, worker.work);
|
||||
struct i2c_msg msg;
|
||||
u8 msgbuf[256];
|
||||
int ret = 0;
|
||||
|
||||
msg.addr = I2C_CLIENT_END;
|
||||
msg.buf = msgbuf;
|
||||
|
||||
switch (tu->regs[TU_REG_CMD]) {
|
||||
case TU_CMD_READ_BYTES:
|
||||
msg.addr = tu->regs[TU_REG_DATAL];
|
||||
msg.flags = I2C_M_RD;
|
||||
msg.len = tu->regs[TU_REG_DATAH];
|
||||
break;
|
||||
|
||||
case TU_CMD_HOST_NOTIFY:
|
||||
msg.addr = 0x08;
|
||||
msg.flags = 0;
|
||||
msg.len = 3;
|
||||
msgbuf[0] = tu->client->addr;
|
||||
msgbuf[1] = tu->regs[TU_REG_DATAL];
|
||||
msgbuf[2] = tu->regs[TU_REG_DATAH];
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (msg.addr != I2C_CLIENT_END) {
|
||||
ret = i2c_transfer(tu->client->adapter, &msg, 1);
|
||||
/* convert '0 msgs transferred' to errno */
|
||||
ret = (ret == 0) ? -EIO : ret;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(&tu->client->dev, "CMD%02X failed (%d)\n", tu->regs[TU_REG_CMD], ret);
|
||||
|
||||
clear_bit(TU_FLAG_IN_PROCESS, &tu->flags);
|
||||
}
|
||||
|
||||
static int i2c_slave_testunit_slave_cb(struct i2c_client *client,
|
||||
enum i2c_slave_event event, u8 *val)
|
||||
{
|
||||
struct testunit_data *tu = i2c_get_clientdata(client);
|
||||
int ret = 0;
|
||||
|
||||
switch (event) {
|
||||
case I2C_SLAVE_WRITE_RECEIVED:
|
||||
if (test_bit(TU_FLAG_IN_PROCESS, &tu->flags))
|
||||
return -EBUSY;
|
||||
|
||||
if (tu->reg_idx < TU_NUM_REGS)
|
||||
tu->regs[tu->reg_idx] = *val;
|
||||
else
|
||||
ret = -EMSGSIZE;
|
||||
|
||||
if (tu->reg_idx <= TU_NUM_REGS)
|
||||
tu->reg_idx++;
|
||||
|
||||
/* TU_REG_CMD always written at this point */
|
||||
if (tu->regs[TU_REG_CMD] >= TU_NUM_CMDS)
|
||||
ret = -EINVAL;
|
||||
|
||||
break;
|
||||
|
||||
case I2C_SLAVE_STOP:
|
||||
if (tu->reg_idx == TU_NUM_REGS) {
|
||||
set_bit(TU_FLAG_IN_PROCESS, &tu->flags);
|
||||
queue_delayed_work(system_long_wq, &tu->worker,
|
||||
msecs_to_jiffies(10 * tu->regs[TU_REG_DELAY]));
|
||||
}
|
||||
fallthrough;
|
||||
|
||||
case I2C_SLAVE_WRITE_REQUESTED:
|
||||
tu->reg_idx = 0;
|
||||
break;
|
||||
|
||||
case I2C_SLAVE_READ_REQUESTED:
|
||||
case I2C_SLAVE_READ_PROCESSED:
|
||||
*val = TU_CUR_VERSION;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int i2c_slave_testunit_probe(struct i2c_client *client)
|
||||
{
|
||||
struct testunit_data *tu;
|
||||
|
||||
tu = devm_kzalloc(&client->dev, sizeof(struct testunit_data), GFP_KERNEL);
|
||||
if (!tu)
|
||||
return -ENOMEM;
|
||||
|
||||
tu->client = client;
|
||||
i2c_set_clientdata(client, tu);
|
||||
INIT_DELAYED_WORK(&tu->worker, i2c_slave_testunit_work);
|
||||
|
||||
return i2c_slave_register(client, i2c_slave_testunit_slave_cb);
|
||||
};
|
||||
|
||||
static int i2c_slave_testunit_remove(struct i2c_client *client)
|
||||
{
|
||||
struct testunit_data *tu = i2c_get_clientdata(client);
|
||||
|
||||
cancel_delayed_work_sync(&tu->worker);
|
||||
i2c_slave_unregister(client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id i2c_slave_testunit_id[] = {
|
||||
{ "slave-testunit", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, i2c_slave_testunit_id);
|
||||
|
||||
static struct i2c_driver i2c_slave_testunit_driver = {
|
||||
.driver = {
|
||||
.name = "i2c-slave-testunit",
|
||||
},
|
||||
.probe_new = i2c_slave_testunit_probe,
|
||||
.remove = i2c_slave_testunit_remove,
|
||||
.id_table = i2c_slave_testunit_id,
|
||||
};
|
||||
module_i2c_driver(i2c_slave_testunit_driver);
|
||||
|
||||
MODULE_AUTHOR("Wolfram Sang <wsa@sang-engineering.com>");
|
||||
MODULE_DESCRIPTION("I2C slave mode test unit");
|
||||
MODULE_LICENSE("GPL v2");
|
@@ -197,6 +197,113 @@ EXPORT_SYMBOL_GPL(i2c_handle_smbus_alert);
|
||||
|
||||
module_i2c_driver(smbalert_driver);
|
||||
|
||||
#if IS_ENABLED(CONFIG_I2C_SLAVE)
|
||||
#define SMBUS_HOST_NOTIFY_LEN 3
|
||||
struct i2c_slave_host_notify_status {
|
||||
u8 index;
|
||||
u8 addr;
|
||||
};
|
||||
|
||||
static int i2c_slave_host_notify_cb(struct i2c_client *client,
|
||||
enum i2c_slave_event event, u8 *val)
|
||||
{
|
||||
struct i2c_slave_host_notify_status *status = client->dev.platform_data;
|
||||
|
||||
switch (event) {
|
||||
case I2C_SLAVE_WRITE_RECEIVED:
|
||||
/* We only retrieve the first byte received (addr)
|
||||
* since there is currently no support to retrieve the data
|
||||
* parameter from the client.
|
||||
*/
|
||||
if (status->index == 0)
|
||||
status->addr = *val;
|
||||
if (status->index < U8_MAX)
|
||||
status->index++;
|
||||
break;
|
||||
case I2C_SLAVE_STOP:
|
||||
if (status->index == SMBUS_HOST_NOTIFY_LEN)
|
||||
i2c_handle_smbus_host_notify(client->adapter,
|
||||
status->addr);
|
||||
fallthrough;
|
||||
case I2C_SLAVE_WRITE_REQUESTED:
|
||||
status->index = 0;
|
||||
break;
|
||||
case I2C_SLAVE_READ_REQUESTED:
|
||||
case I2C_SLAVE_READ_PROCESSED:
|
||||
*val = 0xff;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* i2c_new_slave_host_notify_device - get a client for SMBus host-notify support
|
||||
* @adapter: the target adapter
|
||||
* Context: can sleep
|
||||
*
|
||||
* Setup handling of the SMBus host-notify protocol on a given I2C bus segment.
|
||||
*
|
||||
* Handling is done by creating a device and its callback and handling data
|
||||
* received via the SMBus host-notify address (0x8)
|
||||
*
|
||||
* This returns the client, which should be ultimately freed using
|
||||
* i2c_free_slave_host_notify_device(); or an ERRPTR to indicate an error.
|
||||
*/
|
||||
struct i2c_client *i2c_new_slave_host_notify_device(struct i2c_adapter *adapter)
|
||||
{
|
||||
struct i2c_board_info host_notify_board_info = {
|
||||
I2C_BOARD_INFO("smbus_host_notify", 0x08),
|
||||
.flags = I2C_CLIENT_SLAVE,
|
||||
};
|
||||
struct i2c_slave_host_notify_status *status;
|
||||
struct i2c_client *client;
|
||||
int ret;
|
||||
|
||||
status = kzalloc(sizeof(struct i2c_slave_host_notify_status),
|
||||
GFP_KERNEL);
|
||||
if (!status)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
host_notify_board_info.platform_data = status;
|
||||
|
||||
client = i2c_new_client_device(adapter, &host_notify_board_info);
|
||||
if (IS_ERR(client)) {
|
||||
kfree(status);
|
||||
return client;
|
||||
}
|
||||
|
||||
ret = i2c_slave_register(client, i2c_slave_host_notify_cb);
|
||||
if (ret) {
|
||||
i2c_unregister_device(client);
|
||||
kfree(status);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_new_slave_host_notify_device);
|
||||
|
||||
/**
|
||||
* i2c_free_slave_host_notify_device - free the client for SMBus host-notify
|
||||
* support
|
||||
* @client: the client to free
|
||||
* Context: can sleep
|
||||
*
|
||||
* Free the i2c_client allocated via i2c_new_slave_host_notify_device
|
||||
*/
|
||||
void i2c_free_slave_host_notify_device(struct i2c_client *client)
|
||||
{
|
||||
if (IS_ERR_OR_NULL(client))
|
||||
return;
|
||||
|
||||
i2c_slave_unregister(client);
|
||||
kfree(client->dev.platform_data);
|
||||
i2c_unregister_device(client);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_free_slave_host_notify_device);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* SPD is not part of SMBus but we include it here for convenience as the
|
||||
* target systems are the same.
|
||||
|
@@ -85,18 +85,14 @@ static int i2c_mux_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
mux->control = devm_mux_control_get(dev, NULL);
|
||||
if (IS_ERR(mux->control)) {
|
||||
if (PTR_ERR(mux->control) != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to get control-mux\n");
|
||||
return PTR_ERR(mux->control);
|
||||
}
|
||||
if (IS_ERR(mux->control))
|
||||
return dev_err_probe(dev, PTR_ERR(mux->control),
|
||||
"failed to get control-mux\n");
|
||||
|
||||
parent = mux_parent_adapter(dev);
|
||||
if (IS_ERR(parent)) {
|
||||
if (PTR_ERR(parent) != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to get i2c-parent adapter\n");
|
||||
return PTR_ERR(parent);
|
||||
}
|
||||
if (IS_ERR(parent))
|
||||
return dev_err_probe(dev, PTR_ERR(parent),
|
||||
"failed to get i2c-parent adapter\n");
|
||||
|
||||
children = of_get_child_count(np);
|
||||
|
||||
|
@@ -171,13 +171,9 @@ static int i2c_mux_reg_probe(struct platform_device *pdev)
|
||||
sizeof(mux->data));
|
||||
} else {
|
||||
ret = i2c_mux_reg_probe_dt(mux, pdev);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
return ret;
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Error parsing device tree");
|
||||
return ret;
|
||||
}
|
||||
if (ret < 0)
|
||||
return dev_err_probe(&pdev->dev, ret,
|
||||
"Error parsing device tree");
|
||||
}
|
||||
|
||||
parent = i2c_get_adapter(mux->data.parent);
|
||||
|
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
@@ -89,6 +90,7 @@ struct at24_data {
|
||||
|
||||
struct nvmem_device *nvmem;
|
||||
struct regulator *vcc_reg;
|
||||
void (*read_post)(unsigned int off, char *buf, size_t count);
|
||||
|
||||
/*
|
||||
* Some chips tie up multiple I2C addresses; dummy devices reserve
|
||||
@@ -121,6 +123,7 @@ MODULE_PARM_DESC(at24_write_timeout, "Time (in ms) to try writes (default 25)");
|
||||
struct at24_chip_data {
|
||||
u32 byte_len;
|
||||
u8 flags;
|
||||
void (*read_post)(unsigned int off, char *buf, size_t count);
|
||||
};
|
||||
|
||||
#define AT24_CHIP_DATA(_name, _len, _flags) \
|
||||
@@ -128,6 +131,32 @@ struct at24_chip_data {
|
||||
.byte_len = _len, .flags = _flags, \
|
||||
}
|
||||
|
||||
#define AT24_CHIP_DATA_CB(_name, _len, _flags, _read_post) \
|
||||
static const struct at24_chip_data _name = { \
|
||||
.byte_len = _len, .flags = _flags, \
|
||||
.read_post = _read_post, \
|
||||
}
|
||||
|
||||
static void at24_read_post_vaio(unsigned int off, char *buf, size_t count)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (capable(CAP_SYS_ADMIN))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Hide VAIO private settings to regular users:
|
||||
* - BIOS passwords: bytes 0x00 to 0x0f
|
||||
* - UUID: bytes 0x10 to 0x1f
|
||||
* - Serial number: 0xc0 to 0xdf
|
||||
*/
|
||||
for (i = 0; i < count; i++) {
|
||||
if ((off + i <= 0x1f) ||
|
||||
(off + i >= 0xc0 && off + i <= 0xdf))
|
||||
buf[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* needs 8 addresses as A0-A2 are ignored */
|
||||
AT24_CHIP_DATA(at24_data_24c00, 128 / 8, AT24_FLAG_TAKE8ADDR);
|
||||
/* old variants can't be handled with this generic entry! */
|
||||
@@ -144,6 +173,10 @@ AT24_CHIP_DATA(at24_data_24mac602, 64 / 8,
|
||||
/* spd is a 24c02 in memory DIMMs */
|
||||
AT24_CHIP_DATA(at24_data_spd, 2048 / 8,
|
||||
AT24_FLAG_READONLY | AT24_FLAG_IRUGO);
|
||||
/* 24c02_vaio is a 24c02 on some Sony laptops */
|
||||
AT24_CHIP_DATA_CB(at24_data_24c02_vaio, 2048 / 8,
|
||||
AT24_FLAG_READONLY | AT24_FLAG_IRUGO,
|
||||
at24_read_post_vaio);
|
||||
AT24_CHIP_DATA(at24_data_24c04, 4096 / 8, 0);
|
||||
AT24_CHIP_DATA(at24_data_24cs04, 16,
|
||||
AT24_FLAG_SERIAL | AT24_FLAG_READONLY);
|
||||
@@ -177,6 +210,7 @@ static const struct i2c_device_id at24_ids[] = {
|
||||
{ "24mac402", (kernel_ulong_t)&at24_data_24mac402 },
|
||||
{ "24mac602", (kernel_ulong_t)&at24_data_24mac602 },
|
||||
{ "spd", (kernel_ulong_t)&at24_data_spd },
|
||||
{ "24c02-vaio", (kernel_ulong_t)&at24_data_24c02_vaio },
|
||||
{ "24c04", (kernel_ulong_t)&at24_data_24c04 },
|
||||
{ "24cs04", (kernel_ulong_t)&at24_data_24cs04 },
|
||||
{ "24c08", (kernel_ulong_t)&at24_data_24c08 },
|
||||
@@ -388,7 +422,7 @@ static int at24_read(void *priv, unsigned int off, void *val, size_t count)
|
||||
struct at24_data *at24;
|
||||
struct device *dev;
|
||||
char *buf = val;
|
||||
int ret;
|
||||
int i, ret;
|
||||
|
||||
at24 = priv;
|
||||
dev = at24_base_client_dev(at24);
|
||||
@@ -411,22 +445,22 @@ static int at24_read(void *priv, unsigned int off, void *val, size_t count)
|
||||
*/
|
||||
mutex_lock(&at24->lock);
|
||||
|
||||
while (count) {
|
||||
ret = at24_regmap_read(at24, buf, off, count);
|
||||
for (i = 0; count; i += ret, count -= ret) {
|
||||
ret = at24_regmap_read(at24, buf + i, off + i, count);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&at24->lock);
|
||||
pm_runtime_put(dev);
|
||||
return ret;
|
||||
}
|
||||
buf += ret;
|
||||
off += ret;
|
||||
count -= ret;
|
||||
}
|
||||
|
||||
mutex_unlock(&at24->lock);
|
||||
|
||||
pm_runtime_put(dev);
|
||||
|
||||
if (unlikely(at24->read_post))
|
||||
at24->read_post(off, buf, i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -654,6 +688,7 @@ static int at24_probe(struct i2c_client *client)
|
||||
at24->byte_len = byte_len;
|
||||
at24->page_size = page_size;
|
||||
at24->flags = flags;
|
||||
at24->read_post = cdata->read_post;
|
||||
at24->num_addresses = num_addresses;
|
||||
at24->offset_adj = at24_get_offset_adj(flags, byte_len);
|
||||
at24->client[0].client = client;
|
||||
@@ -678,8 +713,30 @@ static int at24_probe(struct i2c_client *client)
|
||||
return err;
|
||||
}
|
||||
|
||||
nvmem_config.name = dev_name(dev);
|
||||
/*
|
||||
* If the 'label' property is not present for the AT24 EEPROM,
|
||||
* then nvmem_config.id is initialised to NVMEM_DEVID_AUTO,
|
||||
* and this will append the 'devid' to the name of the NVMEM
|
||||
* device. This is purely legacy and the AT24 driver has always
|
||||
* defaulted to this. However, if the 'label' property is
|
||||
* present then this means that the name is specified by the
|
||||
* firmware and this name should be used verbatim and so it is
|
||||
* not necessary to append the 'devid'.
|
||||
*/
|
||||
if (device_property_present(dev, "label")) {
|
||||
nvmem_config.id = NVMEM_DEVID_NONE;
|
||||
err = device_property_read_string(dev, "label",
|
||||
&nvmem_config.name);
|
||||
if (err)
|
||||
return err;
|
||||
} else {
|
||||
nvmem_config.id = NVMEM_DEVID_AUTO;
|
||||
nvmem_config.name = dev_name(dev);
|
||||
}
|
||||
|
||||
nvmem_config.type = NVMEM_TYPE_EEPROM;
|
||||
nvmem_config.dev = dev;
|
||||
nvmem_config.id = NVMEM_DEVID_AUTO;
|
||||
nvmem_config.read_only = !writable;
|
||||
nvmem_config.root_only = !(flags & AT24_FLAG_IRUGO);
|
||||
nvmem_config.owner = THIS_MODULE;
|
||||
|
@@ -76,7 +76,7 @@ static ssize_t eeprom_read(struct file *filp, struct kobject *kobj,
|
||||
struct bin_attribute *bin_attr,
|
||||
char *buf, loff_t off, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(kobj_to_dev(kobj));
|
||||
struct i2c_client *client = kobj_to_i2c_client(kobj);
|
||||
struct eeprom_data *data = i2c_get_clientdata(client);
|
||||
u8 slice;
|
||||
|
||||
|
@@ -669,6 +669,16 @@ config RTC_DRV_RV3028
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-rv3028.
|
||||
|
||||
config RTC_DRV_RV3032
|
||||
tristate "Micro Crystal RV3032"
|
||||
select REGMAP_I2C
|
||||
help
|
||||
If you say yes here you get support for the Micro Crystal
|
||||
RV3032.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-rv3032.
|
||||
|
||||
config RTC_DRV_RV8803
|
||||
tristate "Micro Crystal RV8803, Epson RX8900"
|
||||
help
|
||||
|
@@ -141,6 +141,7 @@ obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o
|
||||
obj-$(CONFIG_RTC_DRV_RTD119X) += rtc-rtd119x.o
|
||||
obj-$(CONFIG_RTC_DRV_RV3028) += rtc-rv3028.o
|
||||
obj-$(CONFIG_RTC_DRV_RV3029C2) += rtc-rv3029c2.o
|
||||
obj-$(CONFIG_RTC_DRV_RV3032) += rtc-rv3032.o
|
||||
obj-$(CONFIG_RTC_DRV_RV8803) += rtc-rv8803.o
|
||||
obj-$(CONFIG_RTC_DRV_RX4581) += rtc-rx4581.o
|
||||
obj-$(CONFIG_RTC_DRV_RX6110) += rtc-rx6110.o
|
||||
|
@@ -1006,6 +1006,7 @@ static int cmos_suspend(struct device *dev)
|
||||
enable_irq_wake(cmos->irq);
|
||||
}
|
||||
|
||||
memset(&cmos->saved_wkalrm, 0, sizeof(struct rtc_wkalrm));
|
||||
cmos_read_alarm(dev, &cmos->saved_wkalrm);
|
||||
|
||||
dev_dbg(dev, "suspend%s, ctrl %02x\n",
|
||||
@@ -1054,6 +1055,7 @@ static void cmos_check_wkalrm(struct device *dev)
|
||||
return;
|
||||
}
|
||||
|
||||
memset(¤t_alarm, 0, sizeof(struct rtc_wkalrm));
|
||||
cmos_read_alarm(dev, ¤t_alarm);
|
||||
t_current_expires = rtc_tm_to_time64(¤t_alarm.time);
|
||||
t_saved_expires = rtc_tm_to_time64(&cmos->saved_wkalrm.time);
|
||||
|
@@ -122,6 +122,9 @@ enum ds_type {
|
||||
#define RX8130_REG_FLAG_AF BIT(3)
|
||||
#define RX8130_REG_CONTROL0 0x1e
|
||||
#define RX8130_REG_CONTROL0_AIE BIT(3)
|
||||
#define RX8130_REG_CONTROL1 0x1f
|
||||
#define RX8130_REG_CONTROL1_INIEN BIT(4)
|
||||
#define RX8130_REG_CONTROL1_CHGEN BIT(5)
|
||||
|
||||
#define MCP794XX_REG_CONTROL 0x07
|
||||
# define MCP794XX_BIT_ALM0_EN 0x10
|
||||
@@ -153,6 +156,7 @@ enum ds_type {
|
||||
#define DS1388_REG_CONTROL 0x0c
|
||||
# define DS1388_BIT_RST BIT(0)
|
||||
# define DS1388_BIT_WDE BIT(1)
|
||||
# define DS1388_BIT_nEOSC BIT(7)
|
||||
|
||||
/* negative offset step is -2.034ppm */
|
||||
#define M41TXX_NEG_OFFSET_STEP_PPB 2034
|
||||
@@ -190,6 +194,15 @@ struct chip_desc {
|
||||
u16 trickle_charger_reg;
|
||||
u8 (*do_trickle_setup)(struct ds1307 *, u32,
|
||||
bool);
|
||||
/* Does the RTC require trickle-resistor-ohms to select the value of
|
||||
* the resistor between Vcc and Vbackup?
|
||||
*/
|
||||
bool requires_trickle_resistor;
|
||||
/* Some RTC's batteries and supercaps were charged by default, others
|
||||
* allow charging but were not configured previously to do so.
|
||||
* Remember this behavior to stay backwards compatible.
|
||||
*/
|
||||
bool charge_default;
|
||||
};
|
||||
|
||||
static const struct chip_desc chips[last_ds_type];
|
||||
@@ -352,6 +365,10 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t)
|
||||
regmap_update_bits(ds1307->regmap, DS1340_REG_FLAG,
|
||||
DS1340_BIT_OSF, 0);
|
||||
break;
|
||||
case ds_1388:
|
||||
regmap_update_bits(ds1307->regmap, DS1388_REG_FLAG,
|
||||
DS1388_BIT_OSF, 0);
|
||||
break;
|
||||
case mcp794xx:
|
||||
/*
|
||||
* these bits were cleared when preparing the date/time
|
||||
@@ -507,6 +524,8 @@ static u8 do_trickle_setup_ds1339(struct ds1307 *ds1307, u32 ohms, bool diode)
|
||||
u8 setup = (diode) ? DS1307_TRICKLE_CHARGER_DIODE :
|
||||
DS1307_TRICKLE_CHARGER_NO_DIODE;
|
||||
|
||||
setup |= DS13XX_TRICKLE_CHARGER_MAGIC;
|
||||
|
||||
switch (ohms) {
|
||||
case 250:
|
||||
setup |= DS1307_TRICKLE_CHARGER_250_OHM;
|
||||
@@ -525,6 +544,16 @@ static u8 do_trickle_setup_ds1339(struct ds1307 *ds1307, u32 ohms, bool diode)
|
||||
return setup;
|
||||
}
|
||||
|
||||
static u8 do_trickle_setup_rx8130(struct ds1307 *ds1307, u32 ohms, bool diode)
|
||||
{
|
||||
/* make sure that the backup battery is enabled */
|
||||
u8 setup = RX8130_REG_CONTROL1_INIEN;
|
||||
if (diode)
|
||||
setup |= RX8130_REG_CONTROL1_CHGEN;
|
||||
|
||||
return setup;
|
||||
}
|
||||
|
||||
static irqreturn_t rx8130_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct ds1307 *ds1307 = dev_id;
|
||||
@@ -979,6 +1008,8 @@ static const struct chip_desc chips[last_ds_type] = {
|
||||
.bbsqi_bit = DS1339_BIT_BBSQI,
|
||||
.trickle_charger_reg = 0x10,
|
||||
.do_trickle_setup = &do_trickle_setup_ds1339,
|
||||
.requires_trickle_resistor = true,
|
||||
.charge_default = true,
|
||||
},
|
||||
[ds_1340] = {
|
||||
.century_reg = DS1307_REG_HOUR,
|
||||
@@ -986,6 +1017,8 @@ static const struct chip_desc chips[last_ds_type] = {
|
||||
.century_bit = DS1340_BIT_CENTURY,
|
||||
.do_trickle_setup = &do_trickle_setup_ds1339,
|
||||
.trickle_charger_reg = 0x08,
|
||||
.requires_trickle_resistor = true,
|
||||
.charge_default = true,
|
||||
},
|
||||
[ds_1341] = {
|
||||
.century_reg = DS1307_REG_MONTH,
|
||||
@@ -1009,6 +1042,8 @@ static const struct chip_desc chips[last_ds_type] = {
|
||||
.offset = 0x10,
|
||||
.irq_handler = rx8130_irq,
|
||||
.rtc_ops = &rx8130_rtc_ops,
|
||||
.trickle_charger_reg = RX8130_REG_CONTROL1,
|
||||
.do_trickle_setup = &do_trickle_setup_rx8130,
|
||||
},
|
||||
[m41t0] = {
|
||||
.rtc_ops = &m41txx_rtc_ops,
|
||||
@@ -1293,18 +1328,37 @@ static int ds1307_nvram_write(void *priv, unsigned int offset, void *val,
|
||||
static u8 ds1307_trickle_init(struct ds1307 *ds1307,
|
||||
const struct chip_desc *chip)
|
||||
{
|
||||
u32 ohms;
|
||||
bool diode = true;
|
||||
u32 ohms, chargeable;
|
||||
bool diode = chip->charge_default;
|
||||
|
||||
if (!chip->do_trickle_setup)
|
||||
return 0;
|
||||
|
||||
if (device_property_read_u32(ds1307->dev, "trickle-resistor-ohms",
|
||||
&ohms))
|
||||
&ohms) && chip->requires_trickle_resistor)
|
||||
return 0;
|
||||
|
||||
if (device_property_read_bool(ds1307->dev, "trickle-diode-disable"))
|
||||
/* aux-voltage-chargeable takes precedence over the deprecated
|
||||
* trickle-diode-disable
|
||||
*/
|
||||
if (!device_property_read_u32(ds1307->dev, "aux-voltage-chargeable",
|
||||
&chargeable)) {
|
||||
switch (chargeable) {
|
||||
case 0:
|
||||
diode = false;
|
||||
break;
|
||||
case 1:
|
||||
diode = true;
|
||||
break;
|
||||
default:
|
||||
dev_warn(ds1307->dev,
|
||||
"unsupported aux-voltage-chargeable value\n");
|
||||
break;
|
||||
}
|
||||
} else if (device_property_read_bool(ds1307->dev,
|
||||
"trickle-diode-disable")) {
|
||||
diode = false;
|
||||
}
|
||||
|
||||
return chip->do_trickle_setup(ds1307, ohms, diode);
|
||||
}
|
||||
@@ -1758,7 +1812,6 @@ static int ds1307_probe(struct i2c_client *client,
|
||||
trickle_charger_setup = pdata->trickle_charger_setup;
|
||||
|
||||
if (trickle_charger_setup && chip->trickle_charger_reg) {
|
||||
trickle_charger_setup |= DS13XX_TRICKLE_CHARGER_MAGIC;
|
||||
dev_dbg(ds1307->dev,
|
||||
"writing trickle charger info 0x%x to 0x%x\n",
|
||||
trickle_charger_setup, chip->trickle_charger_reg);
|
||||
@@ -1881,6 +1934,19 @@ static int ds1307_probe(struct i2c_client *client,
|
||||
DS1307_REG_HOUR << 4 | 0x08, hour);
|
||||
}
|
||||
break;
|
||||
case ds_1388:
|
||||
err = regmap_read(ds1307->regmap, DS1388_REG_CONTROL, &tmp);
|
||||
if (err) {
|
||||
dev_dbg(ds1307->dev, "read error %d\n", err);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* oscillator off? turn it on, so clock can tick. */
|
||||
if (tmp & DS1388_BIT_nEOSC) {
|
||||
tmp &= ~DS1388_BIT_nEOSC;
|
||||
regmap_write(ds1307->regmap, DS1388_REG_CONTROL, tmp);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@@ -193,12 +193,12 @@ ds1685_rtc_begin_data_access(struct ds1685_priv *rtc)
|
||||
rtc->write(rtc, RTC_CTRL_B,
|
||||
(rtc->read(rtc, RTC_CTRL_B) | RTC_CTRL_B_SET));
|
||||
|
||||
/* Switch to Bank 1 */
|
||||
ds1685_rtc_switch_to_bank1(rtc);
|
||||
|
||||
/* Read Ext Ctrl 4A and check the INCR bit to avoid a lockout. */
|
||||
while (rtc->read(rtc, RTC_EXT_CTRL_4A) & RTC_CTRL_4A_INCR)
|
||||
cpu_relax();
|
||||
|
||||
/* Switch to Bank 1 */
|
||||
ds1685_rtc_switch_to_bank1(rtc);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -213,7 +213,7 @@ static inline void
|
||||
ds1685_rtc_end_data_access(struct ds1685_priv *rtc)
|
||||
{
|
||||
/* Switch back to Bank 0 */
|
||||
ds1685_rtc_switch_to_bank1(rtc);
|
||||
ds1685_rtc_switch_to_bank0(rtc);
|
||||
|
||||
/* Clear the SET bit in Ctrl B */
|
||||
rtc->write(rtc, RTC_CTRL_B,
|
||||
|
@@ -3,7 +3,7 @@
|
||||
* Freescale FlexTimer Module (FTM) alarm device driver.
|
||||
*
|
||||
* Copyright 2014 Freescale Semiconductor, Inc.
|
||||
* Copyright 2019 NXP
|
||||
* Copyright 2019-2020 NXP
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -312,7 +312,7 @@ static const struct of_device_id ftm_rtc_match[] = {
|
||||
};
|
||||
|
||||
static const struct acpi_device_id ftm_imx_acpi_ids[] = {
|
||||
{"NXP0011",},
|
||||
{"NXP0014",},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, ftm_imx_acpi_ids);
|
||||
|
@@ -65,7 +65,6 @@ static const struct rtc_class_ops meson_vrtc_ops = {
|
||||
static int meson_vrtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct meson_vrtc_data *vrtc;
|
||||
int ret;
|
||||
|
||||
vrtc = devm_kzalloc(&pdev->dev, sizeof(*vrtc), GFP_KERNEL);
|
||||
if (!vrtc)
|
||||
@@ -84,11 +83,7 @@ static int meson_vrtc_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(vrtc->rtc);
|
||||
|
||||
vrtc->rtc->ops = &meson_vrtc_ops;
|
||||
ret = rtc_register_device(vrtc->rtc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
return rtc_register_device(vrtc->rtc);
|
||||
}
|
||||
|
||||
static int __maybe_unused meson_vrtc_suspend(struct device *dev)
|
||||
|
@@ -31,7 +31,8 @@ static int mtk_rtc_write_trigger(struct mt6397_rtc *rtc)
|
||||
MTK_RTC_POLL_DELAY_US,
|
||||
MTK_RTC_POLL_TIMEOUT);
|
||||
if (ret < 0)
|
||||
dev_err(rtc->dev, "failed to write WRTGE: %d\n", ret);
|
||||
dev_err(rtc->rtc_dev->dev.parent,
|
||||
"failed to write WRTGR: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@@ -559,7 +559,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
|
||||
pcf2127->rtc->set_start_time = true; /* Sets actual start to 1970 */
|
||||
pcf2127->rtc->uie_unsupported = 1;
|
||||
|
||||
if (alarm_irq >= 0) {
|
||||
if (alarm_irq > 0) {
|
||||
ret = devm_request_threaded_irq(dev, alarm_irq, NULL,
|
||||
pcf2127_rtc_irq,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
@@ -570,7 +570,7 @@ static int pcf2127_probe(struct device *dev, struct regmap *regmap,
|
||||
}
|
||||
}
|
||||
|
||||
if (alarm_irq >= 0 || device_property_read_bool(dev, "wakeup-source")) {
|
||||
if (alarm_irq > 0 || device_property_read_bool(dev, "wakeup-source")) {
|
||||
device_init_wakeup(dev, true);
|
||||
pcf2127->rtc->ops = &pcf2127_rtc_alrm_ops;
|
||||
}
|
||||
|
@@ -75,8 +75,6 @@ static int r9701_get_datetime(struct device *dev, struct rtc_time *dt)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
memset(dt, 0, sizeof(*dt));
|
||||
|
||||
dt->tm_sec = bcd2bin(buf[0]); /* RSECCNT */
|
||||
dt->tm_min = bcd2bin(buf[1]); /* RMINCNT */
|
||||
dt->tm_hour = bcd2bin(buf[2]); /* RHRCNT */
|
||||
@@ -85,20 +83,12 @@ static int r9701_get_datetime(struct device *dev, struct rtc_time *dt)
|
||||
dt->tm_mon = bcd2bin(buf[4]) - 1; /* RMONCNT */
|
||||
dt->tm_year = bcd2bin(buf[5]) + 100; /* RYRCNT */
|
||||
|
||||
/* the rtc device may contain illegal values on power up
|
||||
* according to the data sheet. make sure they are valid.
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int r9701_set_datetime(struct device *dev, struct rtc_time *dt)
|
||||
{
|
||||
int ret, year;
|
||||
|
||||
year = dt->tm_year + 1900;
|
||||
if (year >= 2100 || year < 2000)
|
||||
return -EINVAL;
|
||||
int ret;
|
||||
|
||||
ret = write_reg(dev, RHRCNT, bin2bcd(dt->tm_hour));
|
||||
ret = ret ? ret : write_reg(dev, RMINCNT, bin2bcd(dt->tm_min));
|
||||
@@ -106,7 +96,6 @@ static int r9701_set_datetime(struct device *dev, struct rtc_time *dt)
|
||||
ret = ret ? ret : write_reg(dev, RDAYCNT, bin2bcd(dt->tm_mday));
|
||||
ret = ret ? ret : write_reg(dev, RMONCNT, bin2bcd(dt->tm_mon + 1));
|
||||
ret = ret ? ret : write_reg(dev, RYRCNT, bin2bcd(dt->tm_year - 100));
|
||||
ret = ret ? ret : write_reg(dev, RWKCNT, 1 << dt->tm_wday);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -119,7 +108,6 @@ static const struct rtc_class_ops r9701_rtc_ops = {
|
||||
static int r9701_probe(struct spi_device *spi)
|
||||
{
|
||||
struct rtc_device *rtc;
|
||||
struct rtc_time dt;
|
||||
unsigned char tmp;
|
||||
int res;
|
||||
|
||||
@@ -130,35 +118,16 @@ static int r9701_probe(struct spi_device *spi)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* The device seems to be present. Now check if the registers
|
||||
* contain invalid values. If so, try to write a default date:
|
||||
* 2000/1/1 00:00:00
|
||||
*/
|
||||
if (r9701_get_datetime(&spi->dev, &dt)) {
|
||||
dev_info(&spi->dev, "trying to repair invalid date/time\n");
|
||||
dt.tm_sec = 0;
|
||||
dt.tm_min = 0;
|
||||
dt.tm_hour = 0;
|
||||
dt.tm_mday = 1;
|
||||
dt.tm_mon = 0;
|
||||
dt.tm_year = 100;
|
||||
|
||||
if (r9701_set_datetime(&spi->dev, &dt) ||
|
||||
r9701_get_datetime(&spi->dev, &dt)) {
|
||||
dev_err(&spi->dev, "cannot repair RTC register\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
rtc = devm_rtc_device_register(&spi->dev, "r9701",
|
||||
&r9701_rtc_ops, THIS_MODULE);
|
||||
rtc = devm_rtc_allocate_device(&spi->dev);
|
||||
if (IS_ERR(rtc))
|
||||
return PTR_ERR(rtc);
|
||||
|
||||
spi_set_drvdata(spi, rtc);
|
||||
rtc->ops = &r9701_rtc_ops;
|
||||
rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
|
||||
rtc->range_max = RTC_TIMESTAMP_END_2099;
|
||||
|
||||
return 0;
|
||||
return rtc_register_device(rtc);
|
||||
}
|
||||
|
||||
static struct spi_driver r9701_driver = {
|
||||
|
@@ -366,15 +366,15 @@ static const struct rtc_class_ops rs5c313_rtc_ops = {
|
||||
|
||||
static int rs5c313_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct rtc_device *rtc = devm_rtc_device_register(&pdev->dev, "rs5c313",
|
||||
&rs5c313_rtc_ops, THIS_MODULE);
|
||||
struct rtc_device *rtc;
|
||||
|
||||
if (IS_ERR(rtc))
|
||||
return PTR_ERR(rtc);
|
||||
rs5c313_init_port();
|
||||
rs5c313_check_xstp_bit();
|
||||
|
||||
platform_set_drvdata(pdev, rtc);
|
||||
rtc = devm_rtc_device_register(&pdev->dev, "rs5c313", &rs5c313_rtc_ops,
|
||||
THIS_MODULE);
|
||||
|
||||
return 0;
|
||||
return PTR_ERR_OR_ZERO(rtc);
|
||||
}
|
||||
|
||||
static struct platform_driver rs5c313_rtc_platform_driver = {
|
||||
@@ -384,27 +384,7 @@ static struct platform_driver rs5c313_rtc_platform_driver = {
|
||||
.probe = rs5c313_rtc_probe,
|
||||
};
|
||||
|
||||
static int __init rs5c313_rtc_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = platform_driver_register(&rs5c313_rtc_platform_driver);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
rs5c313_init_port();
|
||||
rs5c313_check_xstp_bit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit rs5c313_rtc_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&rs5c313_rtc_platform_driver);
|
||||
}
|
||||
|
||||
module_init(rs5c313_rtc_init);
|
||||
module_exit(rs5c313_rtc_exit);
|
||||
module_platform_driver(rs5c313_rtc_platform_driver);
|
||||
|
||||
MODULE_AUTHOR("kogiidena , Nobuhiro Iwamatsu <iwamatsu@nigauri.org>");
|
||||
MODULE_DESCRIPTION("Ricoh RS5C313 RTC device driver");
|
||||
|
@@ -71,6 +71,7 @@
|
||||
|
||||
#define RV3028_EVT_CTRL_TSR BIT(2)
|
||||
|
||||
#define RV3028_EEPROM_CMD_UPDATE 0x11
|
||||
#define RV3028_EEPROM_CMD_WRITE 0x21
|
||||
#define RV3028_EEPROM_CMD_READ 0x22
|
||||
|
||||
@@ -95,7 +96,7 @@ struct rv3028_data {
|
||||
#endif
|
||||
};
|
||||
|
||||
static u16 rv3028_trickle_resistors[] = {1000, 3000, 6000, 11000};
|
||||
static u16 rv3028_trickle_resistors[] = {3000, 5000, 9000, 15000};
|
||||
|
||||
static ssize_t timestamp0_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
@@ -171,6 +172,88 @@ static const struct attribute_group rv3028_attr_group = {
|
||||
.attrs = rv3028_attrs,
|
||||
};
|
||||
|
||||
static int rv3028_exit_eerd(struct rv3028_data *rv3028, u32 eerd)
|
||||
{
|
||||
if (eerd)
|
||||
return 0;
|
||||
|
||||
return regmap_update_bits(rv3028->regmap, RV3028_CTRL1, RV3028_CTRL1_EERD, 0);
|
||||
}
|
||||
|
||||
static int rv3028_enter_eerd(struct rv3028_data *rv3028, u32 *eerd)
|
||||
{
|
||||
u32 ctrl1, status;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(rv3028->regmap, RV3028_CTRL1, &ctrl1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*eerd = ctrl1 & RV3028_CTRL1_EERD;
|
||||
if (*eerd)
|
||||
return 0;
|
||||
|
||||
ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL1,
|
||||
RV3028_CTRL1_EERD, RV3028_CTRL1_EERD);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read_poll_timeout(rv3028->regmap, RV3028_STATUS, status,
|
||||
!(status & RV3028_STATUS_EEBUSY),
|
||||
RV3028_EEBUSY_POLL, RV3028_EEBUSY_TIMEOUT);
|
||||
if (ret) {
|
||||
rv3028_exit_eerd(rv3028, *eerd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rv3028_update_eeprom(struct rv3028_data *rv3028, u32 eerd)
|
||||
{
|
||||
u32 status;
|
||||
int ret;
|
||||
|
||||
ret = regmap_write(rv3028->regmap, RV3028_EEPROM_CMD, 0x0);
|
||||
if (ret)
|
||||
goto exit_eerd;
|
||||
|
||||
ret = regmap_write(rv3028->regmap, RV3028_EEPROM_CMD, RV3028_EEPROM_CMD_UPDATE);
|
||||
if (ret)
|
||||
goto exit_eerd;
|
||||
|
||||
usleep_range(63000, RV3028_EEBUSY_TIMEOUT);
|
||||
|
||||
ret = regmap_read_poll_timeout(rv3028->regmap, RV3028_STATUS, status,
|
||||
!(status & RV3028_STATUS_EEBUSY),
|
||||
RV3028_EEBUSY_POLL, RV3028_EEBUSY_TIMEOUT);
|
||||
|
||||
exit_eerd:
|
||||
rv3028_exit_eerd(rv3028, eerd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rv3028_update_cfg(struct rv3028_data *rv3028, unsigned int reg,
|
||||
unsigned int mask, unsigned int val)
|
||||
{
|
||||
u32 eerd;
|
||||
int ret;
|
||||
|
||||
ret = rv3028_enter_eerd(rv3028, &eerd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_update_bits(rv3028->regmap, reg, mask, val);
|
||||
if (ret) {
|
||||
rv3028_exit_eerd(rv3028, eerd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return rv3028_update_eeprom(rv3028, eerd);
|
||||
}
|
||||
|
||||
static irqreturn_t rv3028_handle_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct rv3028_data *rv3028 = dev_id;
|
||||
@@ -404,17 +487,32 @@ static int rv3028_read_offset(struct device *dev, long *offset)
|
||||
static int rv3028_set_offset(struct device *dev, long offset)
|
||||
{
|
||||
struct rv3028_data *rv3028 = dev_get_drvdata(dev);
|
||||
u32 eerd;
|
||||
int ret;
|
||||
|
||||
offset = clamp(offset, -244141L, 243187L) * 1000;
|
||||
offset = DIV_ROUND_CLOSEST(offset, OFFSET_STEP_PPT);
|
||||
|
||||
ret = regmap_write(rv3028->regmap, RV3028_OFFSET, offset >> 1);
|
||||
if (ret < 0)
|
||||
ret = rv3028_enter_eerd(rv3028, &eerd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return regmap_update_bits(rv3028->regmap, RV3028_BACKUP, BIT(7),
|
||||
offset << 7);
|
||||
ret = regmap_write(rv3028->regmap, RV3028_OFFSET, offset >> 1);
|
||||
if (ret < 0)
|
||||
goto exit_eerd;
|
||||
|
||||
ret = regmap_update_bits(rv3028->regmap, RV3028_BACKUP, BIT(7),
|
||||
offset << 7);
|
||||
if (ret < 0)
|
||||
goto exit_eerd;
|
||||
|
||||
return rv3028_update_eeprom(rv3028, eerd);
|
||||
|
||||
exit_eerd:
|
||||
rv3028_exit_eerd(rv3028, eerd);
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static int rv3028_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
|
||||
@@ -451,49 +549,36 @@ static int rv3028_nvram_read(void *priv, unsigned int offset, void *val,
|
||||
static int rv3028_eeprom_write(void *priv, unsigned int offset, void *val,
|
||||
size_t bytes)
|
||||
{
|
||||
u32 status, ctrl1;
|
||||
int i, ret, err;
|
||||
struct rv3028_data *rv3028 = priv;
|
||||
u32 status, eerd;
|
||||
int i, ret;
|
||||
u8 *buf = val;
|
||||
|
||||
ret = regmap_read(priv, RV3028_CTRL1, &ctrl1);
|
||||
ret = rv3028_enter_eerd(rv3028, &eerd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!(ctrl1 & RV3028_CTRL1_EERD)) {
|
||||
ret = regmap_update_bits(priv, RV3028_CTRL1,
|
||||
RV3028_CTRL1_EERD, RV3028_CTRL1_EERD);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read_poll_timeout(priv, RV3028_STATUS, status,
|
||||
!(status & RV3028_STATUS_EEBUSY),
|
||||
RV3028_EEBUSY_POLL,
|
||||
RV3028_EEBUSY_TIMEOUT);
|
||||
if (ret)
|
||||
goto restore_eerd;
|
||||
}
|
||||
|
||||
for (i = 0; i < bytes; i++) {
|
||||
ret = regmap_write(priv, RV3028_EEPROM_ADDR, offset + i);
|
||||
ret = regmap_write(rv3028->regmap, RV3028_EEPROM_ADDR, offset + i);
|
||||
if (ret)
|
||||
goto restore_eerd;
|
||||
|
||||
ret = regmap_write(priv, RV3028_EEPROM_DATA, buf[i]);
|
||||
ret = regmap_write(rv3028->regmap, RV3028_EEPROM_DATA, buf[i]);
|
||||
if (ret)
|
||||
goto restore_eerd;
|
||||
|
||||
ret = regmap_write(priv, RV3028_EEPROM_CMD, 0x0);
|
||||
ret = regmap_write(rv3028->regmap, RV3028_EEPROM_CMD, 0x0);
|
||||
if (ret)
|
||||
goto restore_eerd;
|
||||
|
||||
ret = regmap_write(priv, RV3028_EEPROM_CMD,
|
||||
ret = regmap_write(rv3028->regmap, RV3028_EEPROM_CMD,
|
||||
RV3028_EEPROM_CMD_WRITE);
|
||||
if (ret)
|
||||
goto restore_eerd;
|
||||
|
||||
usleep_range(RV3028_EEBUSY_POLL, RV3028_EEBUSY_TIMEOUT);
|
||||
|
||||
ret = regmap_read_poll_timeout(priv, RV3028_STATUS, status,
|
||||
ret = regmap_read_poll_timeout(rv3028->regmap, RV3028_STATUS, status,
|
||||
!(status & RV3028_STATUS_EEBUSY),
|
||||
RV3028_EEBUSY_POLL,
|
||||
RV3028_EEBUSY_TIMEOUT);
|
||||
@@ -502,13 +587,7 @@ static int rv3028_eeprom_write(void *priv, unsigned int offset, void *val,
|
||||
}
|
||||
|
||||
restore_eerd:
|
||||
if (!(ctrl1 & RV3028_CTRL1_EERD))
|
||||
{
|
||||
err = regmap_update_bits(priv, RV3028_CTRL1, RV3028_CTRL1_EERD,
|
||||
0);
|
||||
if (err && !ret)
|
||||
ret = err;
|
||||
}
|
||||
rv3028_exit_eerd(rv3028, eerd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -516,63 +595,44 @@ restore_eerd:
|
||||
static int rv3028_eeprom_read(void *priv, unsigned int offset, void *val,
|
||||
size_t bytes)
|
||||
{
|
||||
u32 status, ctrl1, data;
|
||||
int i, ret, err;
|
||||
struct rv3028_data *rv3028 = priv;
|
||||
u32 status, eerd, data;
|
||||
int i, ret;
|
||||
u8 *buf = val;
|
||||
|
||||
ret = regmap_read(priv, RV3028_CTRL1, &ctrl1);
|
||||
ret = rv3028_enter_eerd(rv3028, &eerd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!(ctrl1 & RV3028_CTRL1_EERD)) {
|
||||
ret = regmap_update_bits(priv, RV3028_CTRL1,
|
||||
RV3028_CTRL1_EERD, RV3028_CTRL1_EERD);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read_poll_timeout(priv, RV3028_STATUS, status,
|
||||
!(status & RV3028_STATUS_EEBUSY),
|
||||
RV3028_EEBUSY_POLL,
|
||||
RV3028_EEBUSY_TIMEOUT);
|
||||
if (ret)
|
||||
goto restore_eerd;
|
||||
}
|
||||
|
||||
for (i = 0; i < bytes; i++) {
|
||||
ret = regmap_write(priv, RV3028_EEPROM_ADDR, offset + i);
|
||||
ret = regmap_write(rv3028->regmap, RV3028_EEPROM_ADDR, offset + i);
|
||||
if (ret)
|
||||
goto restore_eerd;
|
||||
|
||||
ret = regmap_write(priv, RV3028_EEPROM_CMD, 0x0);
|
||||
ret = regmap_write(rv3028->regmap, RV3028_EEPROM_CMD, 0x0);
|
||||
if (ret)
|
||||
goto restore_eerd;
|
||||
|
||||
ret = regmap_write(priv, RV3028_EEPROM_CMD,
|
||||
ret = regmap_write(rv3028->regmap, RV3028_EEPROM_CMD,
|
||||
RV3028_EEPROM_CMD_READ);
|
||||
if (ret)
|
||||
goto restore_eerd;
|
||||
|
||||
ret = regmap_read_poll_timeout(priv, RV3028_STATUS, status,
|
||||
ret = regmap_read_poll_timeout(rv3028->regmap, RV3028_STATUS, status,
|
||||
!(status & RV3028_STATUS_EEBUSY),
|
||||
RV3028_EEBUSY_POLL,
|
||||
RV3028_EEBUSY_TIMEOUT);
|
||||
if (ret)
|
||||
goto restore_eerd;
|
||||
|
||||
ret = regmap_read(priv, RV3028_EEPROM_DATA, &data);
|
||||
ret = regmap_read(rv3028->regmap, RV3028_EEPROM_DATA, &data);
|
||||
if (ret)
|
||||
goto restore_eerd;
|
||||
buf[i] = data;
|
||||
}
|
||||
|
||||
restore_eerd:
|
||||
if (!(ctrl1 & RV3028_CTRL1_EERD))
|
||||
{
|
||||
err = regmap_update_bits(priv, RV3028_CTRL1, RV3028_CTRL1_EERD,
|
||||
0);
|
||||
if (err && !ret)
|
||||
ret = err;
|
||||
}
|
||||
rv3028_exit_eerd(rv3028, eerd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -619,24 +679,23 @@ static int rv3028_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
int i, ret;
|
||||
u32 enabled;
|
||||
struct rv3028_data *rv3028 = clkout_hw_to_rv3028(hw);
|
||||
|
||||
ret = regmap_read(rv3028->regmap, RV3028_CLKOUT, &enabled);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(rv3028->regmap, RV3028_CLKOUT, 0x0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(clkout_rates); i++) {
|
||||
if (clkout_rates[i] == rate) {
|
||||
ret = regmap_update_bits(rv3028->regmap,
|
||||
RV3028_CLKOUT,
|
||||
RV3028_CLKOUT_FD_MASK, i);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
enabled &= RV3028_CLKOUT_CLKOE;
|
||||
|
||||
return regmap_write(rv3028->regmap, RV3028_CLKOUT,
|
||||
RV3028_CLKOUT_CLKSY | RV3028_CLKOUT_CLKOE);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < ARRAY_SIZE(clkout_rates); i++)
|
||||
if (clkout_rates[i] == rate)
|
||||
return rv3028_update_cfg(rv3028, RV3028_CLKOUT, 0xff,
|
||||
RV3028_CLKOUT_CLKSY | enabled | i);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -811,10 +870,8 @@ static int rv3028_probe(struct i2c_client *client)
|
||||
break;
|
||||
|
||||
if (i < ARRAY_SIZE(rv3028_trickle_resistors)) {
|
||||
ret = regmap_update_bits(rv3028->regmap, RV3028_BACKUP,
|
||||
RV3028_BACKUP_TCE |
|
||||
RV3028_BACKUP_TCR_MASK,
|
||||
RV3028_BACKUP_TCE | i);
|
||||
ret = rv3028_update_cfg(rv3028, RV3028_BACKUP, RV3028_BACKUP_TCE |
|
||||
RV3028_BACKUP_TCR_MASK, RV3028_BACKUP_TCE | i);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
@@ -835,7 +892,7 @@ static int rv3028_probe(struct i2c_client *client)
|
||||
|
||||
nvmem_cfg.priv = rv3028->regmap;
|
||||
rtc_nvmem_register(rv3028->rtc, &nvmem_cfg);
|
||||
eeprom_cfg.priv = rv3028->regmap;
|
||||
eeprom_cfg.priv = rv3028;
|
||||
rtc_nvmem_register(rv3028->rtc, &eeprom_cfg);
|
||||
|
||||
rv3028->rtc->max_user_freq = 1;
|
||||
|
925
drivers/rtc/rtc-rv3032.c
Normal file
925
drivers/rtc/rtc-rv3032.c
Normal file
@@ -0,0 +1,925 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* RTC driver for the Micro Crystal RV3032
|
||||
*
|
||||
* Copyright (C) 2020 Micro Crystal SA
|
||||
*
|
||||
* Alexandre Belloni <alexandre.belloni@bootlin.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/rtc.h>
|
||||
|
||||
#define RV3032_SEC 0x01
|
||||
#define RV3032_MIN 0x02
|
||||
#define RV3032_HOUR 0x03
|
||||
#define RV3032_WDAY 0x04
|
||||
#define RV3032_DAY 0x05
|
||||
#define RV3032_MONTH 0x06
|
||||
#define RV3032_YEAR 0x07
|
||||
#define RV3032_ALARM_MIN 0x08
|
||||
#define RV3032_ALARM_HOUR 0x09
|
||||
#define RV3032_ALARM_DAY 0x0A
|
||||
#define RV3032_STATUS 0x0D
|
||||
#define RV3032_TLSB 0x0E
|
||||
#define RV3032_TMSB 0x0F
|
||||
#define RV3032_CTRL1 0x10
|
||||
#define RV3032_CTRL2 0x11
|
||||
#define RV3032_CTRL3 0x12
|
||||
#define RV3032_TS_CTRL 0x13
|
||||
#define RV3032_CLK_IRQ 0x14
|
||||
#define RV3032_EEPROM_ADDR 0x3D
|
||||
#define RV3032_EEPROM_DATA 0x3E
|
||||
#define RV3032_EEPROM_CMD 0x3F
|
||||
#define RV3032_RAM1 0x40
|
||||
#define RV3032_PMU 0xC0
|
||||
#define RV3032_OFFSET 0xC1
|
||||
#define RV3032_CLKOUT1 0xC2
|
||||
#define RV3032_CLKOUT2 0xC3
|
||||
#define RV3032_TREF0 0xC4
|
||||
#define RV3032_TREF1 0xC5
|
||||
|
||||
#define RV3032_STATUS_VLF BIT(0)
|
||||
#define RV3032_STATUS_PORF BIT(1)
|
||||
#define RV3032_STATUS_EVF BIT(2)
|
||||
#define RV3032_STATUS_AF BIT(3)
|
||||
#define RV3032_STATUS_TF BIT(4)
|
||||
#define RV3032_STATUS_UF BIT(5)
|
||||
#define RV3032_STATUS_TLF BIT(6)
|
||||
#define RV3032_STATUS_THF BIT(7)
|
||||
|
||||
#define RV3032_TLSB_CLKF BIT(1)
|
||||
#define RV3032_TLSB_EEBUSY BIT(2)
|
||||
#define RV3032_TLSB_TEMP GENMASK(7, 4)
|
||||
|
||||
#define RV3032_CLKOUT2_HFD_MSK GENMASK(4, 0)
|
||||
#define RV3032_CLKOUT2_FD_MSK GENMASK(6, 5)
|
||||
#define RV3032_CLKOUT2_OS BIT(7)
|
||||
|
||||
#define RV3032_CTRL1_EERD BIT(3)
|
||||
#define RV3032_CTRL1_WADA BIT(5)
|
||||
|
||||
#define RV3032_CTRL2_STOP BIT(0)
|
||||
#define RV3032_CTRL2_EIE BIT(2)
|
||||
#define RV3032_CTRL2_AIE BIT(3)
|
||||
#define RV3032_CTRL2_TIE BIT(4)
|
||||
#define RV3032_CTRL2_UIE BIT(5)
|
||||
#define RV3032_CTRL2_CLKIE BIT(6)
|
||||
#define RV3032_CTRL2_TSE BIT(7)
|
||||
|
||||
#define RV3032_PMU_TCM GENMASK(1, 0)
|
||||
#define RV3032_PMU_TCR GENMASK(3, 2)
|
||||
#define RV3032_PMU_BSM GENMASK(5, 4)
|
||||
#define RV3032_PMU_NCLKE BIT(6)
|
||||
|
||||
#define RV3032_PMU_BSM_DSM 1
|
||||
#define RV3032_PMU_BSM_LSM 2
|
||||
|
||||
#define RV3032_OFFSET_MSK GENMASK(5, 0)
|
||||
|
||||
#define RV3032_EVT_CTRL_TSR BIT(2)
|
||||
|
||||
#define RV3032_EEPROM_CMD_UPDATE 0x11
|
||||
#define RV3032_EEPROM_CMD_WRITE 0x21
|
||||
#define RV3032_EEPROM_CMD_READ 0x22
|
||||
|
||||
#define RV3032_EEPROM_USER 0xCB
|
||||
|
||||
#define RV3032_EEBUSY_POLL 10000
|
||||
#define RV3032_EEBUSY_TIMEOUT 100000
|
||||
|
||||
#define OFFSET_STEP_PPT 238419
|
||||
|
||||
struct rv3032_data {
|
||||
struct regmap *regmap;
|
||||
struct rtc_device *rtc;
|
||||
#ifdef CONFIG_COMMON_CLK
|
||||
struct clk_hw clkout_hw;
|
||||
#endif
|
||||
};
|
||||
|
||||
static u16 rv3032_trickle_resistors[] = {1000, 2000, 7000, 11000};
|
||||
static u16 rv3032_trickle_voltages[] = {0, 1750, 3000, 4400};
|
||||
|
||||
static int rv3032_exit_eerd(struct rv3032_data *rv3032, u32 eerd)
|
||||
{
|
||||
if (eerd)
|
||||
return 0;
|
||||
|
||||
return regmap_update_bits(rv3032->regmap, RV3032_CTRL1, RV3032_CTRL1_EERD, 0);
|
||||
}
|
||||
|
||||
static int rv3032_enter_eerd(struct rv3032_data *rv3032, u32 *eerd)
|
||||
{
|
||||
u32 ctrl1, status;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(rv3032->regmap, RV3032_CTRL1, &ctrl1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*eerd = ctrl1 & RV3032_CTRL1_EERD;
|
||||
if (*eerd)
|
||||
return 0;
|
||||
|
||||
ret = regmap_update_bits(rv3032->regmap, RV3032_CTRL1,
|
||||
RV3032_CTRL1_EERD, RV3032_CTRL1_EERD);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read_poll_timeout(rv3032->regmap, RV3032_TLSB, status,
|
||||
!(status & RV3032_TLSB_EEBUSY),
|
||||
RV3032_EEBUSY_POLL, RV3032_EEBUSY_TIMEOUT);
|
||||
if (ret) {
|
||||
rv3032_exit_eerd(rv3032, *eerd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rv3032_update_cfg(struct rv3032_data *rv3032, unsigned int reg,
|
||||
unsigned int mask, unsigned int val)
|
||||
{
|
||||
u32 status, eerd;
|
||||
int ret;
|
||||
|
||||
ret = rv3032_enter_eerd(rv3032, &eerd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_update_bits(rv3032->regmap, reg, mask, val);
|
||||
if (ret)
|
||||
goto exit_eerd;
|
||||
|
||||
ret = regmap_write(rv3032->regmap, RV3032_EEPROM_CMD, RV3032_EEPROM_CMD_UPDATE);
|
||||
if (ret)
|
||||
goto exit_eerd;
|
||||
|
||||
usleep_range(46000, RV3032_EEBUSY_TIMEOUT);
|
||||
|
||||
ret = regmap_read_poll_timeout(rv3032->regmap, RV3032_TLSB, status,
|
||||
!(status & RV3032_TLSB_EEBUSY),
|
||||
RV3032_EEBUSY_POLL, RV3032_EEBUSY_TIMEOUT);
|
||||
|
||||
exit_eerd:
|
||||
rv3032_exit_eerd(rv3032, eerd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t rv3032_handle_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct rv3032_data *rv3032 = dev_id;
|
||||
unsigned long events = 0;
|
||||
u32 status = 0, ctrl = 0;
|
||||
|
||||
if (regmap_read(rv3032->regmap, RV3032_STATUS, &status) < 0 ||
|
||||
status == 0) {
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
if (status & RV3032_STATUS_TF) {
|
||||
status |= RV3032_STATUS_TF;
|
||||
ctrl |= RV3032_CTRL2_TIE;
|
||||
events |= RTC_PF;
|
||||
}
|
||||
|
||||
if (status & RV3032_STATUS_AF) {
|
||||
status |= RV3032_STATUS_AF;
|
||||
ctrl |= RV3032_CTRL2_AIE;
|
||||
events |= RTC_AF;
|
||||
}
|
||||
|
||||
if (status & RV3032_STATUS_UF) {
|
||||
status |= RV3032_STATUS_UF;
|
||||
ctrl |= RV3032_CTRL2_UIE;
|
||||
events |= RTC_UF;
|
||||
}
|
||||
|
||||
if (events) {
|
||||
rtc_update_irq(rv3032->rtc, 1, events);
|
||||
regmap_update_bits(rv3032->regmap, RV3032_STATUS, status, 0);
|
||||
regmap_update_bits(rv3032->regmap, RV3032_CTRL2, ctrl, 0);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int rv3032_get_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct rv3032_data *rv3032 = dev_get_drvdata(dev);
|
||||
u8 date[7];
|
||||
int ret, status;
|
||||
|
||||
ret = regmap_read(rv3032->regmap, RV3032_STATUS, &status);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (status & (RV3032_STATUS_PORF | RV3032_STATUS_VLF))
|
||||
return -EINVAL;
|
||||
|
||||
ret = regmap_bulk_read(rv3032->regmap, RV3032_SEC, date, sizeof(date));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tm->tm_sec = bcd2bin(date[0] & 0x7f);
|
||||
tm->tm_min = bcd2bin(date[1] & 0x7f);
|
||||
tm->tm_hour = bcd2bin(date[2] & 0x3f);
|
||||
tm->tm_wday = date[3] & 0x7;
|
||||
tm->tm_mday = bcd2bin(date[4] & 0x3f);
|
||||
tm->tm_mon = bcd2bin(date[5] & 0x1f) - 1;
|
||||
tm->tm_year = bcd2bin(date[6]) + 100;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rv3032_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct rv3032_data *rv3032 = dev_get_drvdata(dev);
|
||||
u8 date[7];
|
||||
int ret;
|
||||
|
||||
date[0] = bin2bcd(tm->tm_sec);
|
||||
date[1] = bin2bcd(tm->tm_min);
|
||||
date[2] = bin2bcd(tm->tm_hour);
|
||||
date[3] = tm->tm_wday;
|
||||
date[4] = bin2bcd(tm->tm_mday);
|
||||
date[5] = bin2bcd(tm->tm_mon + 1);
|
||||
date[6] = bin2bcd(tm->tm_year - 100);
|
||||
|
||||
ret = regmap_bulk_write(rv3032->regmap, RV3032_SEC, date,
|
||||
sizeof(date));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_update_bits(rv3032->regmap, RV3032_STATUS,
|
||||
RV3032_STATUS_PORF | RV3032_STATUS_VLF, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rv3032_get_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct rv3032_data *rv3032 = dev_get_drvdata(dev);
|
||||
u8 alarmvals[3];
|
||||
int status, ctrl, ret;
|
||||
|
||||
ret = regmap_bulk_read(rv3032->regmap, RV3032_ALARM_MIN, alarmvals,
|
||||
sizeof(alarmvals));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read(rv3032->regmap, RV3032_STATUS, &status);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read(rv3032->regmap, RV3032_CTRL2, &ctrl);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
alrm->time.tm_sec = 0;
|
||||
alrm->time.tm_min = bcd2bin(alarmvals[0] & 0x7f);
|
||||
alrm->time.tm_hour = bcd2bin(alarmvals[1] & 0x3f);
|
||||
alrm->time.tm_mday = bcd2bin(alarmvals[2] & 0x3f);
|
||||
|
||||
alrm->enabled = !!(ctrl & RV3032_CTRL2_AIE);
|
||||
alrm->pending = (status & RV3032_STATUS_AF) && alrm->enabled;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rv3032_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct rv3032_data *rv3032 = dev_get_drvdata(dev);
|
||||
u8 alarmvals[3];
|
||||
u8 ctrl = 0;
|
||||
int ret;
|
||||
|
||||
/* The alarm has no seconds, round up to nearest minute */
|
||||
if (alrm->time.tm_sec) {
|
||||
time64_t alarm_time = rtc_tm_to_time64(&alrm->time);
|
||||
|
||||
alarm_time += 60 - alrm->time.tm_sec;
|
||||
rtc_time64_to_tm(alarm_time, &alrm->time);
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(rv3032->regmap, RV3032_CTRL2,
|
||||
RV3032_CTRL2_AIE | RV3032_CTRL2_UIE, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
alarmvals[0] = bin2bcd(alrm->time.tm_min);
|
||||
alarmvals[1] = bin2bcd(alrm->time.tm_hour);
|
||||
alarmvals[2] = bin2bcd(alrm->time.tm_mday);
|
||||
|
||||
ret = regmap_update_bits(rv3032->regmap, RV3032_STATUS,
|
||||
RV3032_STATUS_AF, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_bulk_write(rv3032->regmap, RV3032_ALARM_MIN, alarmvals,
|
||||
sizeof(alarmvals));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (alrm->enabled) {
|
||||
if (rv3032->rtc->uie_rtctimer.enabled)
|
||||
ctrl |= RV3032_CTRL2_UIE;
|
||||
if (rv3032->rtc->aie_timer.enabled)
|
||||
ctrl |= RV3032_CTRL2_AIE;
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(rv3032->regmap, RV3032_CTRL2,
|
||||
RV3032_CTRL2_UIE | RV3032_CTRL2_AIE, ctrl);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rv3032_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
||||
{
|
||||
struct rv3032_data *rv3032 = dev_get_drvdata(dev);
|
||||
int ctrl = 0, ret;
|
||||
|
||||
if (enabled) {
|
||||
if (rv3032->rtc->uie_rtctimer.enabled)
|
||||
ctrl |= RV3032_CTRL2_UIE;
|
||||
if (rv3032->rtc->aie_timer.enabled)
|
||||
ctrl |= RV3032_CTRL2_AIE;
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(rv3032->regmap, RV3032_STATUS,
|
||||
RV3032_STATUS_AF | RV3032_STATUS_UF, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_update_bits(rv3032->regmap, RV3032_CTRL2,
|
||||
RV3032_CTRL2_UIE | RV3032_CTRL2_AIE, ctrl);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rv3032_read_offset(struct device *dev, long *offset)
|
||||
{
|
||||
struct rv3032_data *rv3032 = dev_get_drvdata(dev);
|
||||
int ret, value, steps;
|
||||
|
||||
ret = regmap_read(rv3032->regmap, RV3032_OFFSET, &value);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
steps = sign_extend32(FIELD_GET(RV3032_OFFSET_MSK, value), 5);
|
||||
|
||||
*offset = DIV_ROUND_CLOSEST(steps * OFFSET_STEP_PPT, 1000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rv3032_set_offset(struct device *dev, long offset)
|
||||
{
|
||||
struct rv3032_data *rv3032 = dev_get_drvdata(dev);
|
||||
|
||||
offset = clamp(offset, -7629L, 7391L) * 1000;
|
||||
offset = DIV_ROUND_CLOSEST(offset, OFFSET_STEP_PPT);
|
||||
|
||||
return rv3032_update_cfg(rv3032, RV3032_OFFSET, RV3032_OFFSET_MSK,
|
||||
FIELD_PREP(RV3032_OFFSET_MSK, offset));
|
||||
}
|
||||
|
||||
static int rv3032_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct rv3032_data *rv3032 = dev_get_drvdata(dev);
|
||||
int status, val = 0, ret = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case RTC_VL_READ:
|
||||
ret = regmap_read(rv3032->regmap, RV3032_STATUS, &status);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (status & (RV3032_STATUS_PORF | RV3032_STATUS_VLF))
|
||||
val = RTC_VL_DATA_INVALID;
|
||||
return put_user(val, (unsigned int __user *)arg);
|
||||
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
}
|
||||
|
||||
static int rv3032_nvram_write(void *priv, unsigned int offset, void *val, size_t bytes)
|
||||
{
|
||||
return regmap_bulk_write(priv, RV3032_RAM1 + offset, val, bytes);
|
||||
}
|
||||
|
||||
static int rv3032_nvram_read(void *priv, unsigned int offset, void *val, size_t bytes)
|
||||
{
|
||||
return regmap_bulk_read(priv, RV3032_RAM1 + offset, val, bytes);
|
||||
}
|
||||
|
||||
static int rv3032_eeprom_write(void *priv, unsigned int offset, void *val, size_t bytes)
|
||||
{
|
||||
struct rv3032_data *rv3032 = priv;
|
||||
u32 status, eerd;
|
||||
int i, ret;
|
||||
u8 *buf = val;
|
||||
|
||||
ret = rv3032_enter_eerd(rv3032, &eerd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < bytes; i++) {
|
||||
ret = regmap_write(rv3032->regmap, RV3032_EEPROM_ADDR,
|
||||
RV3032_EEPROM_USER + offset + i);
|
||||
if (ret)
|
||||
goto exit_eerd;
|
||||
|
||||
ret = regmap_write(rv3032->regmap, RV3032_EEPROM_DATA, buf[i]);
|
||||
if (ret)
|
||||
goto exit_eerd;
|
||||
|
||||
ret = regmap_write(rv3032->regmap, RV3032_EEPROM_CMD,
|
||||
RV3032_EEPROM_CMD_WRITE);
|
||||
if (ret)
|
||||
goto exit_eerd;
|
||||
|
||||
usleep_range(RV3032_EEBUSY_POLL, RV3032_EEBUSY_TIMEOUT);
|
||||
|
||||
ret = regmap_read_poll_timeout(rv3032->regmap, RV3032_TLSB, status,
|
||||
!(status & RV3032_TLSB_EEBUSY),
|
||||
RV3032_EEBUSY_POLL, RV3032_EEBUSY_TIMEOUT);
|
||||
if (ret)
|
||||
goto exit_eerd;
|
||||
}
|
||||
|
||||
exit_eerd:
|
||||
rv3032_exit_eerd(rv3032, eerd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rv3032_eeprom_read(void *priv, unsigned int offset, void *val, size_t bytes)
|
||||
{
|
||||
struct rv3032_data *rv3032 = priv;
|
||||
u32 status, eerd, data;
|
||||
int i, ret;
|
||||
u8 *buf = val;
|
||||
|
||||
ret = rv3032_enter_eerd(rv3032, &eerd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < bytes; i++) {
|
||||
ret = regmap_write(rv3032->regmap, RV3032_EEPROM_ADDR,
|
||||
RV3032_EEPROM_USER + offset + i);
|
||||
if (ret)
|
||||
goto exit_eerd;
|
||||
|
||||
ret = regmap_write(rv3032->regmap, RV3032_EEPROM_CMD,
|
||||
RV3032_EEPROM_CMD_READ);
|
||||
if (ret)
|
||||
goto exit_eerd;
|
||||
|
||||
ret = regmap_read_poll_timeout(rv3032->regmap, RV3032_TLSB, status,
|
||||
!(status & RV3032_TLSB_EEBUSY),
|
||||
RV3032_EEBUSY_POLL, RV3032_EEBUSY_TIMEOUT);
|
||||
if (ret)
|
||||
goto exit_eerd;
|
||||
|
||||
ret = regmap_read(rv3032->regmap, RV3032_EEPROM_DATA, &data);
|
||||
if (ret)
|
||||
goto exit_eerd;
|
||||
buf[i] = data;
|
||||
}
|
||||
|
||||
exit_eerd:
|
||||
rv3032_exit_eerd(rv3032, eerd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rv3032_trickle_charger_setup(struct device *dev, struct rv3032_data *rv3032)
|
||||
{
|
||||
u32 val, ohms, voltage;
|
||||
int i;
|
||||
|
||||
val = FIELD_PREP(RV3032_PMU_TCM, 1) | FIELD_PREP(RV3032_PMU_BSM, RV3032_PMU_BSM_DSM);
|
||||
if (!device_property_read_u32(dev, "trickle-voltage-millivolt", &voltage)) {
|
||||
for (i = 0; i < ARRAY_SIZE(rv3032_trickle_voltages); i++)
|
||||
if (voltage == rv3032_trickle_voltages[i])
|
||||
break;
|
||||
if (i < ARRAY_SIZE(rv3032_trickle_voltages))
|
||||
val = FIELD_PREP(RV3032_PMU_TCM, i) |
|
||||
FIELD_PREP(RV3032_PMU_BSM, RV3032_PMU_BSM_LSM);
|
||||
}
|
||||
|
||||
if (device_property_read_u32(dev, "trickle-resistor-ohms", &ohms))
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(rv3032_trickle_resistors); i++)
|
||||
if (ohms == rv3032_trickle_resistors[i])
|
||||
break;
|
||||
|
||||
if (i >= ARRAY_SIZE(rv3032_trickle_resistors)) {
|
||||
dev_warn(dev, "invalid trickle resistor value\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return rv3032_update_cfg(rv3032, RV3032_PMU,
|
||||
RV3032_PMU_TCR | RV3032_PMU_TCM | RV3032_PMU_BSM,
|
||||
val | FIELD_PREP(RV3032_PMU_TCR, i));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMMON_CLK
|
||||
#define clkout_hw_to_rv3032(hw) container_of(hw, struct rv3032_data, clkout_hw)
|
||||
|
||||
static int clkout_xtal_rates[] = {
|
||||
32768,
|
||||
1024,
|
||||
64,
|
||||
1,
|
||||
};
|
||||
|
||||
#define RV3032_HFD_STEP 8192
|
||||
|
||||
static unsigned long rv3032_clkout_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
int clkout, ret;
|
||||
struct rv3032_data *rv3032 = clkout_hw_to_rv3032(hw);
|
||||
|
||||
ret = regmap_read(rv3032->regmap, RV3032_CLKOUT2, &clkout);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
|
||||
if (clkout & RV3032_CLKOUT2_OS) {
|
||||
unsigned long rate = FIELD_GET(RV3032_CLKOUT2_HFD_MSK, clkout) << 8;
|
||||
|
||||
ret = regmap_read(rv3032->regmap, RV3032_CLKOUT1, &clkout);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
|
||||
rate += clkout + 1;
|
||||
|
||||
return rate * RV3032_HFD_STEP;
|
||||
}
|
||||
|
||||
return clkout_xtal_rates[FIELD_GET(RV3032_CLKOUT2_FD_MSK, clkout)];
|
||||
}
|
||||
|
||||
static long rv3032_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
int i, hfd;
|
||||
|
||||
if (rate < RV3032_HFD_STEP)
|
||||
for (i = 0; i < ARRAY_SIZE(clkout_xtal_rates); i++)
|
||||
if (clkout_xtal_rates[i] <= rate)
|
||||
return clkout_xtal_rates[i];
|
||||
|
||||
hfd = DIV_ROUND_CLOSEST(rate, RV3032_HFD_STEP);
|
||||
|
||||
return RV3032_HFD_STEP * clamp(hfd, 0, 8192);
|
||||
}
|
||||
|
||||
static int rv3032_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct rv3032_data *rv3032 = clkout_hw_to_rv3032(hw);
|
||||
u32 status, eerd;
|
||||
int i, hfd, ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(clkout_xtal_rates); i++) {
|
||||
if (clkout_xtal_rates[i] == rate) {
|
||||
return rv3032_update_cfg(rv3032, RV3032_CLKOUT2, 0xff,
|
||||
FIELD_PREP(RV3032_CLKOUT2_FD_MSK, i));
|
||||
}
|
||||
}
|
||||
|
||||
hfd = DIV_ROUND_CLOSEST(rate, RV3032_HFD_STEP);
|
||||
hfd = clamp(hfd, 1, 8192) - 1;
|
||||
|
||||
ret = rv3032_enter_eerd(rv3032, &eerd);
|
||||
if (ret)
|
||||
goto exit_eerd;
|
||||
|
||||
ret = regmap_write(rv3032->regmap, RV3032_CLKOUT1, hfd & 0xff);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(rv3032->regmap, RV3032_CLKOUT2, RV3032_CLKOUT2_OS |
|
||||
FIELD_PREP(RV3032_CLKOUT2_HFD_MSK, hfd >> 8));
|
||||
if (ret)
|
||||
goto exit_eerd;
|
||||
|
||||
ret = regmap_write(rv3032->regmap, RV3032_EEPROM_CMD, RV3032_EEPROM_CMD_UPDATE);
|
||||
if (ret)
|
||||
goto exit_eerd;
|
||||
|
||||
usleep_range(46000, RV3032_EEBUSY_TIMEOUT);
|
||||
|
||||
ret = regmap_read_poll_timeout(rv3032->regmap, RV3032_TLSB, status,
|
||||
!(status & RV3032_TLSB_EEBUSY),
|
||||
RV3032_EEBUSY_POLL, RV3032_EEBUSY_TIMEOUT);
|
||||
|
||||
exit_eerd:
|
||||
rv3032_exit_eerd(rv3032, eerd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rv3032_clkout_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct rv3032_data *rv3032 = clkout_hw_to_rv3032(hw);
|
||||
|
||||
return rv3032_update_cfg(rv3032, RV3032_PMU, RV3032_PMU_NCLKE, 0);
|
||||
}
|
||||
|
||||
static void rv3032_clkout_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
struct rv3032_data *rv3032 = clkout_hw_to_rv3032(hw);
|
||||
|
||||
rv3032_update_cfg(rv3032, RV3032_PMU, RV3032_PMU_NCLKE, RV3032_PMU_NCLKE);
|
||||
}
|
||||
|
||||
static int rv3032_clkout_is_prepared(struct clk_hw *hw)
|
||||
{
|
||||
int val, ret;
|
||||
struct rv3032_data *rv3032 = clkout_hw_to_rv3032(hw);
|
||||
|
||||
ret = regmap_read(rv3032->regmap, RV3032_PMU, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return !(val & RV3032_PMU_NCLKE);
|
||||
}
|
||||
|
||||
static const struct clk_ops rv3032_clkout_ops = {
|
||||
.prepare = rv3032_clkout_prepare,
|
||||
.unprepare = rv3032_clkout_unprepare,
|
||||
.is_prepared = rv3032_clkout_is_prepared,
|
||||
.recalc_rate = rv3032_clkout_recalc_rate,
|
||||
.round_rate = rv3032_clkout_round_rate,
|
||||
.set_rate = rv3032_clkout_set_rate,
|
||||
};
|
||||
|
||||
static int rv3032_clkout_register_clk(struct rv3032_data *rv3032,
|
||||
struct i2c_client *client)
|
||||
{
|
||||
int ret;
|
||||
struct clk *clk;
|
||||
struct clk_init_data init;
|
||||
struct device_node *node = client->dev.of_node;
|
||||
|
||||
ret = regmap_update_bits(rv3032->regmap, RV3032_TLSB, RV3032_TLSB_CLKF, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_update_bits(rv3032->regmap, RV3032_CTRL2, RV3032_CTRL2_CLKIE, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(rv3032->regmap, RV3032_CLK_IRQ, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
init.name = "rv3032-clkout";
|
||||
init.ops = &rv3032_clkout_ops;
|
||||
init.flags = 0;
|
||||
init.parent_names = NULL;
|
||||
init.num_parents = 0;
|
||||
rv3032->clkout_hw.init = &init;
|
||||
|
||||
of_property_read_string(node, "clock-output-names", &init.name);
|
||||
|
||||
clk = devm_clk_register(&client->dev, &rv3032->clkout_hw);
|
||||
if (!IS_ERR(clk))
|
||||
of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int rv3032_hwmon_read_temp(struct device *dev, long *mC)
|
||||
{
|
||||
struct rv3032_data *rv3032 = dev_get_drvdata(dev);
|
||||
u8 buf[2];
|
||||
int temp, prev = 0;
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_read(rv3032->regmap, RV3032_TLSB, buf, sizeof(buf));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
temp = sign_extend32(buf[1], 7) << 4;
|
||||
temp |= FIELD_GET(RV3032_TLSB_TEMP, buf[0]);
|
||||
|
||||
/* No blocking or shadowing on RV3032_TLSB and RV3032_TMSB */
|
||||
do {
|
||||
prev = temp;
|
||||
|
||||
ret = regmap_bulk_read(rv3032->regmap, RV3032_TLSB, buf, sizeof(buf));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
temp = sign_extend32(buf[1], 7) << 4;
|
||||
temp |= FIELD_GET(RV3032_TLSB_TEMP, buf[0]);
|
||||
} while (temp != prev);
|
||||
|
||||
*mC = (temp * 1000) / 16;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static umode_t rv3032_hwmon_is_visible(const void *data, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel)
|
||||
{
|
||||
if (type != hwmon_temp)
|
||||
return 0;
|
||||
|
||||
switch (attr) {
|
||||
case hwmon_temp_input:
|
||||
return 0444;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int rv3032_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long *temp)
|
||||
{
|
||||
int err;
|
||||
|
||||
switch (attr) {
|
||||
case hwmon_temp_input:
|
||||
err = rv3032_hwmon_read_temp(dev, temp);
|
||||
break;
|
||||
default:
|
||||
err = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct hwmon_channel_info *rv3032_hwmon_info[] = {
|
||||
HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
|
||||
HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST),
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct hwmon_ops rv3032_hwmon_hwmon_ops = {
|
||||
.is_visible = rv3032_hwmon_is_visible,
|
||||
.read = rv3032_hwmon_read,
|
||||
};
|
||||
|
||||
static const struct hwmon_chip_info rv3032_hwmon_chip_info = {
|
||||
.ops = &rv3032_hwmon_hwmon_ops,
|
||||
.info = rv3032_hwmon_info,
|
||||
};
|
||||
|
||||
static void rv3032_hwmon_register(struct device *dev)
|
||||
{
|
||||
struct rv3032_data *rv3032 = dev_get_drvdata(dev);
|
||||
|
||||
if (!IS_REACHABLE(CONFIG_HWMON))
|
||||
return;
|
||||
|
||||
devm_hwmon_device_register_with_info(dev, "rv3032", rv3032, &rv3032_hwmon_chip_info, NULL);
|
||||
}
|
||||
|
||||
static struct rtc_class_ops rv3032_rtc_ops = {
|
||||
.read_time = rv3032_get_time,
|
||||
.set_time = rv3032_set_time,
|
||||
.read_offset = rv3032_read_offset,
|
||||
.set_offset = rv3032_set_offset,
|
||||
.ioctl = rv3032_ioctl,
|
||||
};
|
||||
|
||||
static const struct regmap_config regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = 0xCA,
|
||||
};
|
||||
|
||||
static int rv3032_probe(struct i2c_client *client)
|
||||
{
|
||||
struct rv3032_data *rv3032;
|
||||
int ret, status;
|
||||
struct nvmem_config nvmem_cfg = {
|
||||
.name = "rv3032_nvram",
|
||||
.word_size = 1,
|
||||
.stride = 1,
|
||||
.size = 16,
|
||||
.type = NVMEM_TYPE_BATTERY_BACKED,
|
||||
.reg_read = rv3032_nvram_read,
|
||||
.reg_write = rv3032_nvram_write,
|
||||
};
|
||||
struct nvmem_config eeprom_cfg = {
|
||||
.name = "rv3032_eeprom",
|
||||
.word_size = 1,
|
||||
.stride = 1,
|
||||
.size = 32,
|
||||
.type = NVMEM_TYPE_EEPROM,
|
||||
.reg_read = rv3032_eeprom_read,
|
||||
.reg_write = rv3032_eeprom_write,
|
||||
};
|
||||
|
||||
rv3032 = devm_kzalloc(&client->dev, sizeof(struct rv3032_data),
|
||||
GFP_KERNEL);
|
||||
if (!rv3032)
|
||||
return -ENOMEM;
|
||||
|
||||
rv3032->regmap = devm_regmap_init_i2c(client, ®map_config);
|
||||
if (IS_ERR(rv3032->regmap))
|
||||
return PTR_ERR(rv3032->regmap);
|
||||
|
||||
i2c_set_clientdata(client, rv3032);
|
||||
|
||||
ret = regmap_read(rv3032->regmap, RV3032_STATUS, &status);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
rv3032->rtc = devm_rtc_allocate_device(&client->dev);
|
||||
if (IS_ERR(rv3032->rtc))
|
||||
return PTR_ERR(rv3032->rtc);
|
||||
|
||||
if (client->irq > 0) {
|
||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL, rv3032_handle_irq,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
"rv3032", rv3032);
|
||||
if (ret) {
|
||||
dev_warn(&client->dev, "unable to request IRQ, alarms disabled\n");
|
||||
client->irq = 0;
|
||||
} else {
|
||||
rv3032_rtc_ops.read_alarm = rv3032_get_alarm;
|
||||
rv3032_rtc_ops.set_alarm = rv3032_set_alarm;
|
||||
rv3032_rtc_ops.alarm_irq_enable = rv3032_alarm_irq_enable;
|
||||
}
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(rv3032->regmap, RV3032_CTRL1,
|
||||
RV3032_CTRL1_WADA, RV3032_CTRL1_WADA);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rv3032_trickle_charger_setup(&client->dev, rv3032);
|
||||
|
||||
rv3032->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
|
||||
rv3032->rtc->range_max = RTC_TIMESTAMP_END_2099;
|
||||
rv3032->rtc->ops = &rv3032_rtc_ops;
|
||||
ret = rtc_register_device(rv3032->rtc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
nvmem_cfg.priv = rv3032;
|
||||
rtc_nvmem_register(rv3032->rtc, &nvmem_cfg);
|
||||
eeprom_cfg.priv = rv3032;
|
||||
rtc_nvmem_register(rv3032->rtc, &eeprom_cfg);
|
||||
|
||||
rv3032->rtc->max_user_freq = 1;
|
||||
|
||||
#ifdef CONFIG_COMMON_CLK
|
||||
rv3032_clkout_register_clk(rv3032, client);
|
||||
#endif
|
||||
|
||||
rv3032_hwmon_register(&client->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id rv3032_of_match[] = {
|
||||
{ .compatible = "microcrystal,rv3032", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rv3032_of_match);
|
||||
|
||||
static struct i2c_driver rv3032_driver = {
|
||||
.driver = {
|
||||
.name = "rtc-rv3032",
|
||||
.of_match_table = of_match_ptr(rv3032_of_match),
|
||||
},
|
||||
.probe_new = rv3032_probe,
|
||||
};
|
||||
module_i2c_driver(rv3032_driver);
|
||||
|
||||
MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@bootlin.com>");
|
||||
MODULE_DESCRIPTION("Micro Crystal RV3032 RTC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@@ -454,13 +454,7 @@ static int rv8803_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
|
||||
static int rv8803_nvram_write(void *priv, unsigned int offset, void *val,
|
||||
size_t bytes)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = rv8803_write_reg(priv, RV8803_RAM, *(u8 *)val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
return rv8803_write_reg(priv, RV8803_RAM, *(u8 *)val);
|
||||
}
|
||||
|
||||
static int rv8803_nvram_read(void *priv, unsigned int offset,
|
||||
|
@@ -11,42 +11,43 @@
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/rtc.h>
|
||||
|
||||
#define RX8010_SEC 0x10
|
||||
#define RX8010_MIN 0x11
|
||||
#define RX8010_HOUR 0x12
|
||||
#define RX8010_WDAY 0x13
|
||||
#define RX8010_MDAY 0x14
|
||||
#define RX8010_MONTH 0x15
|
||||
#define RX8010_YEAR 0x16
|
||||
#define RX8010_RESV17 0x17
|
||||
#define RX8010_ALMIN 0x18
|
||||
#define RX8010_ALHOUR 0x19
|
||||
#define RX8010_ALWDAY 0x1A
|
||||
#define RX8010_TCOUNT0 0x1B
|
||||
#define RX8010_TCOUNT1 0x1C
|
||||
#define RX8010_EXT 0x1D
|
||||
#define RX8010_FLAG 0x1E
|
||||
#define RX8010_CTRL 0x1F
|
||||
#define RX8010_SEC 0x10
|
||||
#define RX8010_MIN 0x11
|
||||
#define RX8010_HOUR 0x12
|
||||
#define RX8010_WDAY 0x13
|
||||
#define RX8010_MDAY 0x14
|
||||
#define RX8010_MONTH 0x15
|
||||
#define RX8010_YEAR 0x16
|
||||
#define RX8010_RESV17 0x17
|
||||
#define RX8010_ALMIN 0x18
|
||||
#define RX8010_ALHOUR 0x19
|
||||
#define RX8010_ALWDAY 0x1A
|
||||
#define RX8010_TCOUNT0 0x1B
|
||||
#define RX8010_TCOUNT1 0x1C
|
||||
#define RX8010_EXT 0x1D
|
||||
#define RX8010_FLAG 0x1E
|
||||
#define RX8010_CTRL 0x1F
|
||||
/* 0x20 to 0x2F are user registers */
|
||||
#define RX8010_RESV30 0x30
|
||||
#define RX8010_RESV31 0x31
|
||||
#define RX8010_IRQ 0x32
|
||||
#define RX8010_RESV30 0x30
|
||||
#define RX8010_RESV31 0x31
|
||||
#define RX8010_IRQ 0x32
|
||||
|
||||
#define RX8010_EXT_WADA BIT(3)
|
||||
#define RX8010_EXT_WADA BIT(3)
|
||||
|
||||
#define RX8010_FLAG_VLF BIT(1)
|
||||
#define RX8010_FLAG_AF BIT(3)
|
||||
#define RX8010_FLAG_TF BIT(4)
|
||||
#define RX8010_FLAG_UF BIT(5)
|
||||
#define RX8010_FLAG_VLF BIT(1)
|
||||
#define RX8010_FLAG_AF BIT(3)
|
||||
#define RX8010_FLAG_TF BIT(4)
|
||||
#define RX8010_FLAG_UF BIT(5)
|
||||
|
||||
#define RX8010_CTRL_AIE BIT(3)
|
||||
#define RX8010_CTRL_UIE BIT(5)
|
||||
#define RX8010_CTRL_STOP BIT(6)
|
||||
#define RX8010_CTRL_TEST BIT(7)
|
||||
#define RX8010_CTRL_AIE BIT(3)
|
||||
#define RX8010_CTRL_UIE BIT(5)
|
||||
#define RX8010_CTRL_STOP BIT(6)
|
||||
#define RX8010_CTRL_TEST BIT(7)
|
||||
|
||||
#define RX8010_ALARM_AE BIT(7)
|
||||
#define RX8010_ALARM_AE BIT(7)
|
||||
|
||||
static const struct i2c_device_id rx8010_id[] = {
|
||||
{ "rx8010", 0 },
|
||||
@@ -61,7 +62,7 @@ static const struct of_device_id rx8010_of_match[] = {
|
||||
MODULE_DEVICE_TABLE(of, rx8010_of_match);
|
||||
|
||||
struct rx8010_data {
|
||||
struct i2c_client *client;
|
||||
struct regmap *regs;
|
||||
struct rtc_device *rtc;
|
||||
u8 ctrlreg;
|
||||
};
|
||||
@@ -70,13 +71,12 @@ static irqreturn_t rx8010_irq_1_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct i2c_client *client = dev_id;
|
||||
struct rx8010_data *rx8010 = i2c_get_clientdata(client);
|
||||
int flagreg;
|
||||
int flagreg, err;
|
||||
|
||||
mutex_lock(&rx8010->rtc->ops_lock);
|
||||
|
||||
flagreg = i2c_smbus_read_byte_data(client, RX8010_FLAG);
|
||||
|
||||
if (flagreg <= 0) {
|
||||
err = regmap_read(rx8010->regs, RX8010_FLAG, &flagreg);
|
||||
if (err) {
|
||||
mutex_unlock(&rx8010->rtc->ops_lock);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
@@ -99,32 +99,29 @@ static irqreturn_t rx8010_irq_1_handler(int irq, void *dev_id)
|
||||
rtc_update_irq(rx8010->rtc, 1, RTC_UF | RTC_IRQF);
|
||||
}
|
||||
|
||||
i2c_smbus_write_byte_data(client, RX8010_FLAG, flagreg);
|
||||
|
||||
err = regmap_write(rx8010->regs, RX8010_FLAG, flagreg);
|
||||
mutex_unlock(&rx8010->rtc->ops_lock);
|
||||
return IRQ_HANDLED;
|
||||
return err ? IRQ_NONE : IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int rx8010_get_time(struct device *dev, struct rtc_time *dt)
|
||||
{
|
||||
struct rx8010_data *rx8010 = dev_get_drvdata(dev);
|
||||
u8 date[7];
|
||||
int flagreg;
|
||||
int err;
|
||||
u8 date[RX8010_YEAR - RX8010_SEC + 1];
|
||||
int flagreg, err;
|
||||
|
||||
flagreg = i2c_smbus_read_byte_data(rx8010->client, RX8010_FLAG);
|
||||
if (flagreg < 0)
|
||||
return flagreg;
|
||||
err = regmap_read(rx8010->regs, RX8010_FLAG, &flagreg);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (flagreg & RX8010_FLAG_VLF) {
|
||||
dev_warn(dev, "Frequency stop detected\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = i2c_smbus_read_i2c_block_data(rx8010->client, RX8010_SEC,
|
||||
7, date);
|
||||
if (err != 7)
|
||||
return err < 0 ? err : -EIO;
|
||||
err = regmap_bulk_read(rx8010->regs, RX8010_SEC, date, sizeof(date));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dt->tm_sec = bcd2bin(date[RX8010_SEC - RX8010_SEC] & 0x7f);
|
||||
dt->tm_min = bcd2bin(date[RX8010_MIN - RX8010_SEC] & 0x7f);
|
||||
@@ -140,22 +137,13 @@ static int rx8010_get_time(struct device *dev, struct rtc_time *dt)
|
||||
static int rx8010_set_time(struct device *dev, struct rtc_time *dt)
|
||||
{
|
||||
struct rx8010_data *rx8010 = dev_get_drvdata(dev);
|
||||
u8 date[7];
|
||||
int ctrl, flagreg;
|
||||
int ret;
|
||||
|
||||
if ((dt->tm_year < 100) || (dt->tm_year > 199))
|
||||
return -EINVAL;
|
||||
u8 date[RX8010_YEAR - RX8010_SEC + 1];
|
||||
int err;
|
||||
|
||||
/* set STOP bit before changing clock/calendar */
|
||||
ctrl = i2c_smbus_read_byte_data(rx8010->client, RX8010_CTRL);
|
||||
if (ctrl < 0)
|
||||
return ctrl;
|
||||
rx8010->ctrlreg = ctrl | RX8010_CTRL_STOP;
|
||||
ret = i2c_smbus_write_byte_data(rx8010->client, RX8010_CTRL,
|
||||
rx8010->ctrlreg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
err = regmap_set_bits(rx8010->regs, RX8010_CTRL, RX8010_CTRL_STOP);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
date[RX8010_SEC - RX8010_SEC] = bin2bcd(dt->tm_sec);
|
||||
date[RX8010_MIN - RX8010_SEC] = bin2bcd(dt->tm_min);
|
||||
@@ -165,66 +153,54 @@ static int rx8010_set_time(struct device *dev, struct rtc_time *dt)
|
||||
date[RX8010_YEAR - RX8010_SEC] = bin2bcd(dt->tm_year - 100);
|
||||
date[RX8010_WDAY - RX8010_SEC] = bin2bcd(1 << dt->tm_wday);
|
||||
|
||||
ret = i2c_smbus_write_i2c_block_data(rx8010->client,
|
||||
RX8010_SEC, 7, date);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
err = regmap_bulk_write(rx8010->regs, RX8010_SEC, date, sizeof(date));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* clear STOP bit after changing clock/calendar */
|
||||
ctrl = i2c_smbus_read_byte_data(rx8010->client, RX8010_CTRL);
|
||||
if (ctrl < 0)
|
||||
return ctrl;
|
||||
rx8010->ctrlreg = ctrl & ~RX8010_CTRL_STOP;
|
||||
ret = i2c_smbus_write_byte_data(rx8010->client, RX8010_CTRL,
|
||||
rx8010->ctrlreg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
err = regmap_clear_bits(rx8010->regs, RX8010_CTRL, RX8010_CTRL_STOP);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
flagreg = i2c_smbus_read_byte_data(rx8010->client, RX8010_FLAG);
|
||||
if (flagreg < 0) {
|
||||
return flagreg;
|
||||
}
|
||||
|
||||
if (flagreg & RX8010_FLAG_VLF)
|
||||
ret = i2c_smbus_write_byte_data(rx8010->client, RX8010_FLAG,
|
||||
flagreg & ~RX8010_FLAG_VLF);
|
||||
err = regmap_clear_bits(rx8010->regs, RX8010_FLAG, RX8010_FLAG_VLF);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rx8010_init_client(struct i2c_client *client)
|
||||
static int rx8010_init(struct device *dev)
|
||||
{
|
||||
struct rx8010_data *rx8010 = i2c_get_clientdata(client);
|
||||
struct rx8010_data *rx8010 = dev_get_drvdata(dev);
|
||||
u8 ctrl[2];
|
||||
int need_clear = 0, err = 0;
|
||||
int need_clear = 0, err;
|
||||
|
||||
/* Initialize reserved registers as specified in datasheet */
|
||||
err = i2c_smbus_write_byte_data(client, RX8010_RESV17, 0xD8);
|
||||
if (err < 0)
|
||||
err = regmap_write(rx8010->regs, RX8010_RESV17, 0xD8);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = i2c_smbus_write_byte_data(client, RX8010_RESV30, 0x00);
|
||||
if (err < 0)
|
||||
err = regmap_write(rx8010->regs, RX8010_RESV30, 0x00);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = i2c_smbus_write_byte_data(client, RX8010_RESV31, 0x08);
|
||||
if (err < 0)
|
||||
err = regmap_write(rx8010->regs, RX8010_RESV31, 0x08);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = i2c_smbus_write_byte_data(client, RX8010_IRQ, 0x00);
|
||||
if (err < 0)
|
||||
err = regmap_write(rx8010->regs, RX8010_IRQ, 0x00);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = i2c_smbus_read_i2c_block_data(rx8010->client, RX8010_FLAG,
|
||||
2, ctrl);
|
||||
if (err != 2)
|
||||
return err < 0 ? err : -EIO;
|
||||
err = regmap_bulk_read(rx8010->regs, RX8010_FLAG, ctrl, 2);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (ctrl[0] & RX8010_FLAG_VLF)
|
||||
dev_warn(&client->dev, "Frequency stop was detected\n");
|
||||
dev_warn(dev, "Frequency stop was detected\n");
|
||||
|
||||
if (ctrl[0] & RX8010_FLAG_AF) {
|
||||
dev_warn(&client->dev, "Alarm was detected\n");
|
||||
dev_warn(dev, "Alarm was detected\n");
|
||||
need_clear = 1;
|
||||
}
|
||||
|
||||
@@ -236,8 +212,8 @@ static int rx8010_init_client(struct i2c_client *client)
|
||||
|
||||
if (need_clear) {
|
||||
ctrl[0] &= ~(RX8010_FLAG_AF | RX8010_FLAG_TF | RX8010_FLAG_UF);
|
||||
err = i2c_smbus_write_byte_data(client, RX8010_FLAG, ctrl[0]);
|
||||
if (err < 0)
|
||||
err = regmap_write(rx8010->regs, RX8010_FLAG, ctrl[0]);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -249,18 +225,16 @@ static int rx8010_init_client(struct i2c_client *client)
|
||||
static int rx8010_read_alarm(struct device *dev, struct rtc_wkalrm *t)
|
||||
{
|
||||
struct rx8010_data *rx8010 = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = rx8010->client;
|
||||
u8 alarmvals[3];
|
||||
int flagreg;
|
||||
int err;
|
||||
int flagreg, err;
|
||||
|
||||
err = i2c_smbus_read_i2c_block_data(client, RX8010_ALMIN, 3, alarmvals);
|
||||
if (err != 3)
|
||||
return err < 0 ? err : -EIO;
|
||||
err = regmap_bulk_read(rx8010->regs, RX8010_ALMIN, alarmvals, 3);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
flagreg = i2c_smbus_read_byte_data(client, RX8010_FLAG);
|
||||
if (flagreg < 0)
|
||||
return flagreg;
|
||||
err = regmap_read(rx8010->regs, RX8010_FLAG, &flagreg);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
t->time.tm_sec = 0;
|
||||
t->time.tm_min = bcd2bin(alarmvals[0] & 0x7f);
|
||||
@@ -277,55 +251,38 @@ static int rx8010_read_alarm(struct device *dev, struct rtc_wkalrm *t)
|
||||
|
||||
static int rx8010_set_alarm(struct device *dev, struct rtc_wkalrm *t)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct rx8010_data *rx8010 = dev_get_drvdata(dev);
|
||||
u8 alarmvals[3];
|
||||
int extreg, flagreg;
|
||||
int err;
|
||||
|
||||
flagreg = i2c_smbus_read_byte_data(client, RX8010_FLAG);
|
||||
if (flagreg < 0) {
|
||||
return flagreg;
|
||||
}
|
||||
|
||||
if (rx8010->ctrlreg & (RX8010_CTRL_AIE | RX8010_CTRL_UIE)) {
|
||||
rx8010->ctrlreg &= ~(RX8010_CTRL_AIE | RX8010_CTRL_UIE);
|
||||
err = i2c_smbus_write_byte_data(rx8010->client, RX8010_CTRL,
|
||||
rx8010->ctrlreg);
|
||||
if (err < 0) {
|
||||
err = regmap_write(rx8010->regs, RX8010_CTRL, rx8010->ctrlreg);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
flagreg &= ~RX8010_FLAG_AF;
|
||||
err = i2c_smbus_write_byte_data(rx8010->client, RX8010_FLAG, flagreg);
|
||||
if (err < 0)
|
||||
err = regmap_clear_bits(rx8010->regs, RX8010_FLAG, RX8010_FLAG_AF);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
alarmvals[0] = bin2bcd(t->time.tm_min);
|
||||
alarmvals[1] = bin2bcd(t->time.tm_hour);
|
||||
alarmvals[2] = bin2bcd(t->time.tm_mday);
|
||||
|
||||
err = i2c_smbus_write_i2c_block_data(rx8010->client, RX8010_ALMIN,
|
||||
2, alarmvals);
|
||||
if (err < 0)
|
||||
err = regmap_bulk_write(rx8010->regs, RX8010_ALMIN, alarmvals, 2);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
extreg = i2c_smbus_read_byte_data(client, RX8010_EXT);
|
||||
if (extreg < 0)
|
||||
return extreg;
|
||||
|
||||
extreg |= RX8010_EXT_WADA;
|
||||
err = i2c_smbus_write_byte_data(rx8010->client, RX8010_EXT, extreg);
|
||||
if (err < 0)
|
||||
err = regmap_clear_bits(rx8010->regs, RX8010_EXT, RX8010_EXT_WADA);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (alarmvals[2] == 0)
|
||||
alarmvals[2] |= RX8010_ALARM_AE;
|
||||
|
||||
err = i2c_smbus_write_byte_data(rx8010->client, RX8010_ALWDAY,
|
||||
alarmvals[2]);
|
||||
if (err < 0)
|
||||
err = regmap_write(rx8010->regs, RX8010_ALWDAY, alarmvals[2]);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (t->enabled) {
|
||||
@@ -335,9 +292,8 @@ static int rx8010_set_alarm(struct device *dev, struct rtc_wkalrm *t)
|
||||
rx8010->ctrlreg |=
|
||||
(RX8010_CTRL_AIE | RX8010_CTRL_UIE);
|
||||
|
||||
err = i2c_smbus_write_byte_data(rx8010->client, RX8010_CTRL,
|
||||
rx8010->ctrlreg);
|
||||
if (err < 0)
|
||||
err = regmap_write(rx8010->regs, RX8010_CTRL, rx8010->ctrlreg);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -347,11 +303,9 @@ static int rx8010_set_alarm(struct device *dev, struct rtc_wkalrm *t)
|
||||
static int rx8010_alarm_irq_enable(struct device *dev,
|
||||
unsigned int enabled)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct rx8010_data *rx8010 = dev_get_drvdata(dev);
|
||||
int flagreg;
|
||||
u8 ctrl;
|
||||
int err;
|
||||
u8 ctrl;
|
||||
|
||||
ctrl = rx8010->ctrlreg;
|
||||
|
||||
@@ -367,20 +321,14 @@ static int rx8010_alarm_irq_enable(struct device *dev,
|
||||
ctrl &= ~RX8010_CTRL_AIE;
|
||||
}
|
||||
|
||||
flagreg = i2c_smbus_read_byte_data(client, RX8010_FLAG);
|
||||
if (flagreg < 0)
|
||||
return flagreg;
|
||||
|
||||
flagreg &= ~RX8010_FLAG_AF;
|
||||
err = i2c_smbus_write_byte_data(rx8010->client, RX8010_FLAG, flagreg);
|
||||
if (err < 0)
|
||||
err = regmap_clear_bits(rx8010->regs, RX8010_FLAG, RX8010_FLAG_AF);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (ctrl != rx8010->ctrlreg) {
|
||||
rx8010->ctrlreg = ctrl;
|
||||
err = i2c_smbus_write_byte_data(rx8010->client, RX8010_CTRL,
|
||||
rx8010->ctrlreg);
|
||||
if (err < 0)
|
||||
err = regmap_write(rx8010->regs, RX8010_CTRL, rx8010->ctrlreg);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -390,14 +338,13 @@ static int rx8010_alarm_irq_enable(struct device *dev,
|
||||
static int rx8010_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct rx8010_data *rx8010 = dev_get_drvdata(dev);
|
||||
int tmp;
|
||||
int flagreg;
|
||||
int tmp, flagreg, err;
|
||||
|
||||
switch (cmd) {
|
||||
case RTC_VL_READ:
|
||||
flagreg = i2c_smbus_read_byte_data(rx8010->client, RX8010_FLAG);
|
||||
if (flagreg < 0)
|
||||
return flagreg;
|
||||
err = regmap_read(rx8010->regs, RX8010_FLAG, &flagreg);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
tmp = flagreg & RX8010_FLAG_VLF ? RTC_VL_DATA_INVALID : 0;
|
||||
return put_user(tmp, (unsigned int __user *)arg);
|
||||
@@ -407,65 +354,72 @@ static int rx8010_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
|
||||
}
|
||||
}
|
||||
|
||||
static struct rtc_class_ops rx8010_rtc_ops = {
|
||||
static const struct rtc_class_ops rx8010_rtc_ops_default = {
|
||||
.read_time = rx8010_get_time,
|
||||
.set_time = rx8010_set_time,
|
||||
.ioctl = rx8010_ioctl,
|
||||
};
|
||||
|
||||
static int rx8010_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static const struct rtc_class_ops rx8010_rtc_ops_alarm = {
|
||||
.read_time = rx8010_get_time,
|
||||
.set_time = rx8010_set_time,
|
||||
.ioctl = rx8010_ioctl,
|
||||
.read_alarm = rx8010_read_alarm,
|
||||
.set_alarm = rx8010_set_alarm,
|
||||
.alarm_irq_enable = rx8010_alarm_irq_enable,
|
||||
};
|
||||
|
||||
static const struct regmap_config rx8010_regmap_config = {
|
||||
.name = "rx8010-rtc",
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
static int rx8010_probe(struct i2c_client *client)
|
||||
{
|
||||
struct i2c_adapter *adapter = client->adapter;
|
||||
struct device *dev = &client->dev;
|
||||
struct rx8010_data *rx8010;
|
||||
int err = 0;
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA
|
||||
| I2C_FUNC_SMBUS_I2C_BLOCK)) {
|
||||
dev_err(&adapter->dev, "doesn't support required functionality\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
rx8010 = devm_kzalloc(&client->dev, sizeof(struct rx8010_data),
|
||||
GFP_KERNEL);
|
||||
rx8010 = devm_kzalloc(dev, sizeof(*rx8010), GFP_KERNEL);
|
||||
if (!rx8010)
|
||||
return -ENOMEM;
|
||||
|
||||
rx8010->client = client;
|
||||
i2c_set_clientdata(client, rx8010);
|
||||
|
||||
err = rx8010_init_client(client);
|
||||
rx8010->regs = devm_regmap_init_i2c(client, &rx8010_regmap_config);
|
||||
if (IS_ERR(rx8010->regs))
|
||||
return PTR_ERR(rx8010->regs);
|
||||
|
||||
err = rx8010_init(dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
rx8010->rtc = devm_rtc_allocate_device(dev);
|
||||
if (IS_ERR(rx8010->rtc))
|
||||
return PTR_ERR(rx8010->rtc);
|
||||
|
||||
if (client->irq > 0) {
|
||||
dev_info(&client->dev, "IRQ %d supplied\n", client->irq);
|
||||
err = devm_request_threaded_irq(&client->dev, client->irq, NULL,
|
||||
dev_info(dev, "IRQ %d supplied\n", client->irq);
|
||||
err = devm_request_threaded_irq(dev, client->irq, NULL,
|
||||
rx8010_irq_1_handler,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
"rx8010", client);
|
||||
|
||||
if (err) {
|
||||
dev_err(&client->dev, "unable to request IRQ\n");
|
||||
client->irq = 0;
|
||||
} else {
|
||||
rx8010_rtc_ops.read_alarm = rx8010_read_alarm;
|
||||
rx8010_rtc_ops.set_alarm = rx8010_set_alarm;
|
||||
rx8010_rtc_ops.alarm_irq_enable = rx8010_alarm_irq_enable;
|
||||
dev_err(dev, "unable to request IRQ\n");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
rx8010->rtc = devm_rtc_device_register(&client->dev, client->name,
|
||||
&rx8010_rtc_ops, THIS_MODULE);
|
||||
|
||||
if (IS_ERR(rx8010->rtc)) {
|
||||
dev_err(&client->dev, "unable to register the class device\n");
|
||||
return PTR_ERR(rx8010->rtc);
|
||||
rx8010->rtc->ops = &rx8010_rtc_ops_alarm;
|
||||
} else {
|
||||
rx8010->rtc->ops = &rx8010_rtc_ops_default;
|
||||
}
|
||||
|
||||
rx8010->rtc->max_user_freq = 1;
|
||||
rx8010->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
|
||||
rx8010->rtc->range_max = RTC_TIMESTAMP_END_2099;
|
||||
|
||||
return 0;
|
||||
return rtc_register_device(rx8010->rtc);
|
||||
}
|
||||
|
||||
static struct i2c_driver rx8010_driver = {
|
||||
@@ -473,7 +427,7 @@ static struct i2c_driver rx8010_driver = {
|
||||
.name = "rtc-rx8010",
|
||||
.of_match_table = of_match_ptr(rx8010_of_match),
|
||||
},
|
||||
.probe = rx8010_probe,
|
||||
.probe_new = rx8010_probe,
|
||||
.id_table = rx8010_id,
|
||||
};
|
||||
|
||||
|
@@ -494,13 +494,8 @@ static int s3c_rtc_probe(struct platform_device *pdev)
|
||||
if (info->data->needs_src_clk) {
|
||||
info->rtc_src_clk = devm_clk_get(&pdev->dev, "rtc_src");
|
||||
if (IS_ERR(info->rtc_src_clk)) {
|
||||
ret = PTR_ERR(info->rtc_src_clk);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev,
|
||||
"failed to find rtc source clock\n");
|
||||
else
|
||||
dev_dbg(&pdev->dev,
|
||||
"probe deferred due to missing rtc src clk\n");
|
||||
ret = dev_err_probe(&pdev->dev, PTR_ERR(info->rtc_src_clk),
|
||||
"failed to find rtc source clock\n");
|
||||
goto err_src_clk;
|
||||
}
|
||||
ret = clk_prepare_enable(info->rtc_src_clk);
|
||||
|
@@ -173,7 +173,7 @@ static int st_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *t)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct rtc_class_ops st_rtc_ops = {
|
||||
static const struct rtc_class_ops st_rtc_ops = {
|
||||
.read_time = st_rtc_read_time,
|
||||
.set_time = st_rtc_set_time,
|
||||
.read_alarm = st_rtc_read_alarm,
|
||||
|
416
fs/ceph/addr.c
416
fs/ceph/addr.c
@@ -182,58 +182,15 @@ static int ceph_releasepage(struct page *page, gfp_t g)
|
||||
return !PagePrivate(page);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read some contiguous pages. If we cross a stripe boundary, shorten
|
||||
* *plen. Return number of bytes read, or error.
|
||||
*/
|
||||
static int ceph_sync_readpages(struct ceph_fs_client *fsc,
|
||||
struct ceph_vino vino,
|
||||
struct ceph_file_layout *layout,
|
||||
u64 off, u64 *plen,
|
||||
u32 truncate_seq, u64 truncate_size,
|
||||
struct page **pages, int num_pages,
|
||||
int page_align)
|
||||
{
|
||||
struct ceph_osd_client *osdc = &fsc->client->osdc;
|
||||
struct ceph_osd_request *req;
|
||||
int rc = 0;
|
||||
|
||||
dout("readpages on ino %llx.%llx on %llu~%llu\n", vino.ino,
|
||||
vino.snap, off, *plen);
|
||||
req = ceph_osdc_new_request(osdc, layout, vino, off, plen, 0, 1,
|
||||
CEPH_OSD_OP_READ, CEPH_OSD_FLAG_READ,
|
||||
NULL, truncate_seq, truncate_size,
|
||||
false);
|
||||
if (IS_ERR(req))
|
||||
return PTR_ERR(req);
|
||||
|
||||
/* it may be a short read due to an object boundary */
|
||||
osd_req_op_extent_osd_data_pages(req, 0,
|
||||
pages, *plen, page_align, false, false);
|
||||
|
||||
dout("readpages final extent is %llu~%llu (%llu bytes align %d)\n",
|
||||
off, *plen, *plen, page_align);
|
||||
|
||||
rc = ceph_osdc_start_request(osdc, req, false);
|
||||
if (!rc)
|
||||
rc = ceph_osdc_wait_request(osdc, req);
|
||||
|
||||
ceph_update_read_latency(&fsc->mdsc->metric, req->r_start_latency,
|
||||
req->r_end_latency, rc);
|
||||
|
||||
ceph_osdc_put_request(req);
|
||||
dout("readpages result %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* read a single page, without unlocking it.
|
||||
*/
|
||||
/* read a single page, without unlocking it. */
|
||||
static int ceph_do_readpage(struct file *filp, struct page *page)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
struct ceph_inode_info *ci = ceph_inode(inode);
|
||||
struct ceph_fs_client *fsc = ceph_inode_to_client(inode);
|
||||
struct ceph_osd_client *osdc = &fsc->client->osdc;
|
||||
struct ceph_osd_request *req;
|
||||
struct ceph_vino vino = ceph_vino(inode);
|
||||
int err = 0;
|
||||
u64 off = page_offset(page);
|
||||
u64 len = PAGE_SIZE;
|
||||
@@ -260,19 +217,33 @@ static int ceph_do_readpage(struct file *filp, struct page *page)
|
||||
if (err == 0)
|
||||
return -EINPROGRESS;
|
||||
|
||||
dout("readpage inode %p file %p page %p index %lu\n",
|
||||
inode, filp, page, page->index);
|
||||
err = ceph_sync_readpages(fsc, ceph_vino(inode),
|
||||
&ci->i_layout, off, &len,
|
||||
ci->i_truncate_seq, ci->i_truncate_size,
|
||||
&page, 1, 0);
|
||||
dout("readpage ino %llx.%llx file %p off %llu len %llu page %p index %lu\n",
|
||||
vino.ino, vino.snap, filp, off, len, page, page->index);
|
||||
req = ceph_osdc_new_request(osdc, &ci->i_layout, vino, off, &len, 0, 1,
|
||||
CEPH_OSD_OP_READ, CEPH_OSD_FLAG_READ, NULL,
|
||||
ci->i_truncate_seq, ci->i_truncate_size,
|
||||
false);
|
||||
if (IS_ERR(req))
|
||||
return PTR_ERR(req);
|
||||
|
||||
osd_req_op_extent_osd_data_pages(req, 0, &page, len, 0, false, false);
|
||||
|
||||
err = ceph_osdc_start_request(osdc, req, false);
|
||||
if (!err)
|
||||
err = ceph_osdc_wait_request(osdc, req);
|
||||
|
||||
ceph_update_read_latency(&fsc->mdsc->metric, req->r_start_latency,
|
||||
req->r_end_latency, err);
|
||||
|
||||
ceph_osdc_put_request(req);
|
||||
dout("readpage result %d\n", err);
|
||||
|
||||
if (err == -ENOENT)
|
||||
err = 0;
|
||||
if (err < 0) {
|
||||
SetPageError(page);
|
||||
ceph_fscache_readpage_cancel(inode, page);
|
||||
if (err == -EBLACKLISTED)
|
||||
fsc->blacklisted = true;
|
||||
if (err == -EBLOCKLISTED)
|
||||
fsc->blocklisted = true;
|
||||
goto out;
|
||||
}
|
||||
if (err < PAGE_SIZE)
|
||||
@@ -312,8 +283,8 @@ static void finish_read(struct ceph_osd_request *req)
|
||||
int i;
|
||||
|
||||
dout("finish_read %p req %p rc %d bytes %d\n", inode, req, rc, bytes);
|
||||
if (rc == -EBLACKLISTED)
|
||||
ceph_inode_to_client(inode)->blacklisted = true;
|
||||
if (rc == -EBLOCKLISTED)
|
||||
ceph_inode_to_client(inode)->blocklisted = true;
|
||||
|
||||
/* unlock all pages, zeroing any data we didn't read */
|
||||
osd_data = osd_req_op_extent_osd_data(req, 0);
|
||||
@@ -619,50 +590,6 @@ static u64 get_writepages_data_length(struct inode *inode,
|
||||
return end > start ? end - start : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* do a synchronous write on N pages
|
||||
*/
|
||||
static int ceph_sync_writepages(struct ceph_fs_client *fsc,
|
||||
struct ceph_vino vino,
|
||||
struct ceph_file_layout *layout,
|
||||
struct ceph_snap_context *snapc,
|
||||
u64 off, u64 len,
|
||||
u32 truncate_seq, u64 truncate_size,
|
||||
struct timespec64 *mtime,
|
||||
struct page **pages, int num_pages)
|
||||
{
|
||||
struct ceph_osd_client *osdc = &fsc->client->osdc;
|
||||
struct ceph_osd_request *req;
|
||||
int rc = 0;
|
||||
int page_align = off & ~PAGE_MASK;
|
||||
|
||||
req = ceph_osdc_new_request(osdc, layout, vino, off, &len, 0, 1,
|
||||
CEPH_OSD_OP_WRITE, CEPH_OSD_FLAG_WRITE,
|
||||
snapc, truncate_seq, truncate_size,
|
||||
true);
|
||||
if (IS_ERR(req))
|
||||
return PTR_ERR(req);
|
||||
|
||||
/* it may be a short write due to an object boundary */
|
||||
osd_req_op_extent_osd_data_pages(req, 0, pages, len, page_align,
|
||||
false, false);
|
||||
dout("writepages %llu~%llu (%llu bytes)\n", off, len, len);
|
||||
|
||||
req->r_mtime = *mtime;
|
||||
rc = ceph_osdc_start_request(osdc, req, true);
|
||||
if (!rc)
|
||||
rc = ceph_osdc_wait_request(osdc, req);
|
||||
|
||||
ceph_update_write_latency(&fsc->mdsc->metric, req->r_start_latency,
|
||||
req->r_end_latency, rc);
|
||||
|
||||
ceph_osdc_put_request(req);
|
||||
if (rc == 0)
|
||||
rc = len;
|
||||
dout("writepages result %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a single page, but leave the page locked.
|
||||
*
|
||||
@@ -671,20 +598,19 @@ static int ceph_sync_writepages(struct ceph_fs_client *fsc,
|
||||
*/
|
||||
static int writepage_nounlock(struct page *page, struct writeback_control *wbc)
|
||||
{
|
||||
struct inode *inode;
|
||||
struct ceph_inode_info *ci;
|
||||
struct ceph_fs_client *fsc;
|
||||
struct inode *inode = page->mapping->host;
|
||||
struct ceph_inode_info *ci = ceph_inode(inode);
|
||||
struct ceph_fs_client *fsc = ceph_inode_to_client(inode);
|
||||
struct ceph_snap_context *snapc, *oldest;
|
||||
loff_t page_off = page_offset(page);
|
||||
int err, len = PAGE_SIZE;
|
||||
int err;
|
||||
loff_t len = PAGE_SIZE;
|
||||
struct ceph_writeback_ctl ceph_wbc;
|
||||
struct ceph_osd_client *osdc = &fsc->client->osdc;
|
||||
struct ceph_osd_request *req;
|
||||
|
||||
dout("writepage %p idx %lu\n", page, page->index);
|
||||
|
||||
inode = page->mapping->host;
|
||||
ci = ceph_inode(inode);
|
||||
fsc = ceph_inode_to_client(inode);
|
||||
|
||||
/* verify this is a writeable snap context */
|
||||
snapc = page_snap_context(page);
|
||||
if (!snapc) {
|
||||
@@ -713,7 +639,7 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc)
|
||||
if (ceph_wbc.i_size < page_off + len)
|
||||
len = ceph_wbc.i_size - page_off;
|
||||
|
||||
dout("writepage %p page %p index %lu on %llu~%u snapc %p seq %lld\n",
|
||||
dout("writepage %p page %p index %lu on %llu~%llu snapc %p seq %lld\n",
|
||||
inode, page, page->index, page_off, len, snapc, snapc->seq);
|
||||
|
||||
if (atomic_long_inc_return(&fsc->writeback_count) >
|
||||
@@ -721,11 +647,33 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc)
|
||||
set_bdi_congested(inode_to_bdi(inode), BLK_RW_ASYNC);
|
||||
|
||||
set_page_writeback(page);
|
||||
err = ceph_sync_writepages(fsc, ceph_vino(inode),
|
||||
&ci->i_layout, snapc, page_off, len,
|
||||
ceph_wbc.truncate_seq,
|
||||
ceph_wbc.truncate_size,
|
||||
&inode->i_mtime, &page, 1);
|
||||
req = ceph_osdc_new_request(osdc, &ci->i_layout, ceph_vino(inode), page_off, &len, 0, 1,
|
||||
CEPH_OSD_OP_WRITE, CEPH_OSD_FLAG_WRITE, snapc,
|
||||
ceph_wbc.truncate_seq, ceph_wbc.truncate_size,
|
||||
true);
|
||||
if (IS_ERR(req)) {
|
||||
redirty_page_for_writepage(wbc, page);
|
||||
end_page_writeback(page);
|
||||
return PTR_ERR(req);
|
||||
}
|
||||
|
||||
/* it may be a short write due to an object boundary */
|
||||
WARN_ON_ONCE(len > PAGE_SIZE);
|
||||
osd_req_op_extent_osd_data_pages(req, 0, &page, len, 0, false, false);
|
||||
dout("writepage %llu~%llu (%llu bytes)\n", page_off, len, len);
|
||||
|
||||
req->r_mtime = inode->i_mtime;
|
||||
err = ceph_osdc_start_request(osdc, req, true);
|
||||
if (!err)
|
||||
err = ceph_osdc_wait_request(osdc, req);
|
||||
|
||||
ceph_update_write_latency(&fsc->mdsc->metric, req->r_start_latency,
|
||||
req->r_end_latency, err);
|
||||
|
||||
ceph_osdc_put_request(req);
|
||||
if (err == 0)
|
||||
err = len;
|
||||
|
||||
if (err < 0) {
|
||||
struct writeback_control tmp_wbc;
|
||||
if (!wbc)
|
||||
@@ -737,8 +685,8 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc)
|
||||
end_page_writeback(page);
|
||||
return err;
|
||||
}
|
||||
if (err == -EBLACKLISTED)
|
||||
fsc->blacklisted = true;
|
||||
if (err == -EBLOCKLISTED)
|
||||
fsc->blocklisted = true;
|
||||
dout("writepage setting page/mapping error %d %p\n",
|
||||
err, page);
|
||||
mapping_set_error(&inode->i_data, err);
|
||||
@@ -801,8 +749,8 @@ static void writepages_finish(struct ceph_osd_request *req)
|
||||
if (rc < 0) {
|
||||
mapping_set_error(mapping, rc);
|
||||
ceph_set_error_write(ci);
|
||||
if (rc == -EBLACKLISTED)
|
||||
fsc->blacklisted = true;
|
||||
if (rc == -EBLOCKLISTED)
|
||||
fsc->blocklisted = true;
|
||||
} else {
|
||||
ceph_clear_error_write(ci);
|
||||
}
|
||||
@@ -962,9 +910,8 @@ retry:
|
||||
max_pages = wsize >> PAGE_SHIFT;
|
||||
|
||||
get_more_pages:
|
||||
pvec_pages = pagevec_lookup_range_nr_tag(&pvec, mapping, &index,
|
||||
end, PAGECACHE_TAG_DIRTY,
|
||||
max_pages - locked_pages);
|
||||
pvec_pages = pagevec_lookup_range_tag(&pvec, mapping, &index,
|
||||
end, PAGECACHE_TAG_DIRTY);
|
||||
dout("pagevec_lookup_range_tag got %d\n", pvec_pages);
|
||||
if (!pvec_pages && !locked_pages)
|
||||
break;
|
||||
@@ -1299,110 +1246,60 @@ static int context_is_writeable_or_written(struct inode *inode,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* We are only allowed to write into/dirty the page if the page is
|
||||
* clean, or already dirty within the same snap context.
|
||||
/**
|
||||
* ceph_find_incompatible - find an incompatible context and return it
|
||||
* @page: page being dirtied
|
||||
*
|
||||
* called with page locked.
|
||||
* return success with page locked,
|
||||
* or any failure (incl -EAGAIN) with page unlocked.
|
||||
* We are only allowed to write into/dirty a page if the page is
|
||||
* clean, or already dirty within the same snap context. Returns a
|
||||
* conflicting context if there is one, NULL if there isn't, or a
|
||||
* negative error code on other errors.
|
||||
*
|
||||
* Must be called with page lock held.
|
||||
*/
|
||||
static int ceph_update_writeable_page(struct file *file,
|
||||
loff_t pos, unsigned len,
|
||||
struct page *page)
|
||||
static struct ceph_snap_context *
|
||||
ceph_find_incompatible(struct page *page)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
struct inode *inode = page->mapping->host;
|
||||
struct ceph_fs_client *fsc = ceph_inode_to_client(inode);
|
||||
struct ceph_inode_info *ci = ceph_inode(inode);
|
||||
loff_t page_off = pos & PAGE_MASK;
|
||||
int pos_in_page = pos & ~PAGE_MASK;
|
||||
int end_in_page = pos_in_page + len;
|
||||
loff_t i_size;
|
||||
int r;
|
||||
struct ceph_snap_context *snapc, *oldest;
|
||||
|
||||
if (READ_ONCE(fsc->mount_state) == CEPH_MOUNT_SHUTDOWN) {
|
||||
dout(" page %p forced umount\n", page);
|
||||
unlock_page(page);
|
||||
return -EIO;
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
|
||||
retry_locked:
|
||||
/* writepages currently holds page lock, but if we change that later, */
|
||||
wait_on_page_writeback(page);
|
||||
for (;;) {
|
||||
struct ceph_snap_context *snapc, *oldest;
|
||||
|
||||
wait_on_page_writeback(page);
|
||||
|
||||
snapc = page_snap_context(page);
|
||||
if (!snapc || snapc == ci->i_head_snapc)
|
||||
break;
|
||||
|
||||
snapc = page_snap_context(page);
|
||||
if (snapc && snapc != ci->i_head_snapc) {
|
||||
/*
|
||||
* this page is already dirty in another (older) snap
|
||||
* context! is it writeable now?
|
||||
*/
|
||||
oldest = get_oldest_context(inode, NULL, NULL);
|
||||
if (snapc->seq > oldest->seq) {
|
||||
/* not writeable -- return it for the caller to deal with */
|
||||
ceph_put_snap_context(oldest);
|
||||
dout(" page %p snapc %p not current or oldest\n",
|
||||
page, snapc);
|
||||
/*
|
||||
* queue for writeback, and wait for snapc to
|
||||
* be writeable or written
|
||||
*/
|
||||
snapc = ceph_get_snap_context(snapc);
|
||||
unlock_page(page);
|
||||
ceph_queue_writeback(inode);
|
||||
r = wait_event_killable(ci->i_cap_wq,
|
||||
context_is_writeable_or_written(inode, snapc));
|
||||
ceph_put_snap_context(snapc);
|
||||
if (r == -ERESTARTSYS)
|
||||
return r;
|
||||
return -EAGAIN;
|
||||
dout(" page %p snapc %p not current or oldest\n", page, snapc);
|
||||
return ceph_get_snap_context(snapc);
|
||||
}
|
||||
ceph_put_snap_context(oldest);
|
||||
|
||||
/* yay, writeable, do it now (without dropping page lock) */
|
||||
dout(" page %p snapc %p not current, but oldest\n",
|
||||
page, snapc);
|
||||
if (!clear_page_dirty_for_io(page))
|
||||
goto retry_locked;
|
||||
r = writepage_nounlock(page, NULL);
|
||||
if (r < 0)
|
||||
goto fail_unlock;
|
||||
goto retry_locked;
|
||||
dout(" page %p snapc %p not current, but oldest\n", page, snapc);
|
||||
if (clear_page_dirty_for_io(page)) {
|
||||
int r = writepage_nounlock(page, NULL);
|
||||
if (r < 0)
|
||||
return ERR_PTR(r);
|
||||
}
|
||||
}
|
||||
|
||||
if (PageUptodate(page)) {
|
||||
dout(" page %p already uptodate\n", page);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* full page? */
|
||||
if (pos_in_page == 0 && len == PAGE_SIZE)
|
||||
return 0;
|
||||
|
||||
/* past end of file? */
|
||||
i_size = i_size_read(inode);
|
||||
|
||||
if (page_off >= i_size ||
|
||||
(pos_in_page == 0 && (pos+len) >= i_size &&
|
||||
end_in_page - pos_in_page != PAGE_SIZE)) {
|
||||
dout(" zeroing %p 0 - %d and %d - %d\n",
|
||||
page, pos_in_page, end_in_page, (int)PAGE_SIZE);
|
||||
zero_user_segments(page,
|
||||
0, pos_in_page,
|
||||
end_in_page, PAGE_SIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* we need to read it. */
|
||||
r = ceph_do_readpage(file, page);
|
||||
if (r < 0) {
|
||||
if (r == -EINPROGRESS)
|
||||
return -EAGAIN;
|
||||
goto fail_unlock;
|
||||
}
|
||||
goto retry_locked;
|
||||
fail_unlock:
|
||||
unlock_page(page);
|
||||
return r;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1414,26 +1311,78 @@ static int ceph_write_begin(struct file *file, struct address_space *mapping,
|
||||
struct page **pagep, void **fsdata)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
struct page *page;
|
||||
struct ceph_inode_info *ci = ceph_inode(inode);
|
||||
struct ceph_snap_context *snapc;
|
||||
struct page *page = NULL;
|
||||
pgoff_t index = pos >> PAGE_SHIFT;
|
||||
int r;
|
||||
int pos_in_page = pos & ~PAGE_MASK;
|
||||
int r = 0;
|
||||
|
||||
do {
|
||||
/* get a page */
|
||||
dout("write_begin file %p inode %p page %p %d~%d\n", file, inode, page, (int)pos, (int)len);
|
||||
|
||||
for (;;) {
|
||||
page = grab_cache_page_write_begin(mapping, index, 0);
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
if (!page) {
|
||||
r = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
|
||||
dout("write_begin file %p inode %p page %p %d~%d\n", file,
|
||||
inode, page, (int)pos, (int)len);
|
||||
|
||||
r = ceph_update_writeable_page(file, pos, len, page);
|
||||
if (r < 0)
|
||||
snapc = ceph_find_incompatible(page);
|
||||
if (snapc) {
|
||||
if (IS_ERR(snapc)) {
|
||||
r = PTR_ERR(snapc);
|
||||
break;
|
||||
}
|
||||
unlock_page(page);
|
||||
put_page(page);
|
||||
else
|
||||
*pagep = page;
|
||||
} while (r == -EAGAIN);
|
||||
page = NULL;
|
||||
ceph_queue_writeback(inode);
|
||||
r = wait_event_killable(ci->i_cap_wq,
|
||||
context_is_writeable_or_written(inode, snapc));
|
||||
ceph_put_snap_context(snapc);
|
||||
if (r != 0)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (PageUptodate(page)) {
|
||||
dout(" page %p already uptodate\n", page);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* In some cases we don't need to read at all:
|
||||
* - full page write
|
||||
* - write that lies completely beyond EOF
|
||||
* - write that covers the the page from start to EOF or beyond it
|
||||
*/
|
||||
if ((pos_in_page == 0 && len == PAGE_SIZE) ||
|
||||
(pos >= i_size_read(inode)) ||
|
||||
(pos_in_page == 0 && (pos + len) >= i_size_read(inode))) {
|
||||
zero_user_segments(page, 0, pos_in_page,
|
||||
pos_in_page + len, PAGE_SIZE);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to read it. If we get back -EINPROGRESS, then the page was
|
||||
* handed off to fscache and it will be unlocked when the read completes.
|
||||
* Refind the page in that case so we can reacquire the page lock. Otherwise
|
||||
* we got a hard error or the read was completed synchronously.
|
||||
*/
|
||||
r = ceph_do_readpage(file, page);
|
||||
if (r != -EINPROGRESS)
|
||||
break;
|
||||
}
|
||||
|
||||
if (r < 0) {
|
||||
if (page) {
|
||||
unlock_page(page);
|
||||
put_page(page);
|
||||
}
|
||||
} else {
|
||||
*pagep = page;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -1522,7 +1471,7 @@ static vm_fault_t ceph_filemap_fault(struct vm_fault *vmf)
|
||||
struct ceph_inode_info *ci = ceph_inode(inode);
|
||||
struct ceph_file_info *fi = vma->vm_file->private_data;
|
||||
struct page *pinned_page = NULL;
|
||||
loff_t off = vmf->pgoff << PAGE_SHIFT;
|
||||
loff_t off = (loff_t)vmf->pgoff << PAGE_SHIFT;
|
||||
int want, got, err;
|
||||
sigset_t oldset;
|
||||
vm_fault_t ret = VM_FAULT_SIGBUS;
|
||||
@@ -1668,6 +1617,8 @@ static vm_fault_t ceph_page_mkwrite(struct vm_fault *vmf)
|
||||
inode_inc_iversion_raw(inode);
|
||||
|
||||
do {
|
||||
struct ceph_snap_context *snapc;
|
||||
|
||||
lock_page(page);
|
||||
|
||||
if (page_mkwrite_check_truncate(page, inode) < 0) {
|
||||
@@ -1676,13 +1627,26 @@ static vm_fault_t ceph_page_mkwrite(struct vm_fault *vmf)
|
||||
break;
|
||||
}
|
||||
|
||||
err = ceph_update_writeable_page(vma->vm_file, off, len, page);
|
||||
if (err >= 0) {
|
||||
snapc = ceph_find_incompatible(page);
|
||||
if (!snapc) {
|
||||
/* success. we'll keep the page locked. */
|
||||
set_page_dirty(page);
|
||||
ret = VM_FAULT_LOCKED;
|
||||
break;
|
||||
}
|
||||
} while (err == -EAGAIN);
|
||||
|
||||
unlock_page(page);
|
||||
|
||||
if (IS_ERR(snapc)) {
|
||||
ret = VM_FAULT_SIGBUS;
|
||||
break;
|
||||
}
|
||||
|
||||
ceph_queue_writeback(inode);
|
||||
err = wait_event_killable(ci->i_cap_wq,
|
||||
context_is_writeable_or_written(inode, snapc));
|
||||
ceph_put_snap_context(snapc);
|
||||
} while (err == 0);
|
||||
|
||||
if (ret == VM_FAULT_LOCKED ||
|
||||
ci->i_inline_version != CEPH_INLINE_NONE) {
|
||||
@@ -2039,16 +2003,16 @@ static int __ceph_pool_perm_get(struct ceph_inode_info *ci,
|
||||
if (err >= 0 || err == -ENOENT)
|
||||
have |= POOL_READ;
|
||||
else if (err != -EPERM) {
|
||||
if (err == -EBLACKLISTED)
|
||||
fsc->blacklisted = true;
|
||||
if (err == -EBLOCKLISTED)
|
||||
fsc->blocklisted = true;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (err2 == 0 || err2 == -EEXIST)
|
||||
have |= POOL_WRITE;
|
||||
else if (err2 != -EPERM) {
|
||||
if (err2 == -EBLACKLISTED)
|
||||
fsc->blacklisted = true;
|
||||
if (err2 == -EBLOCKLISTED)
|
||||
fsc->blocklisted = true;
|
||||
err = err2;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
128
fs/ceph/caps.c
128
fs/ceph/caps.c
@@ -1222,36 +1222,27 @@ struct cap_msg_args {
|
||||
};
|
||||
|
||||
/*
|
||||
* Build and send a cap message to the given MDS.
|
||||
*
|
||||
* Caller should be holding s_mutex.
|
||||
* cap struct size + flock buffer size + inline version + inline data size +
|
||||
* osd_epoch_barrier + oldest_flush_tid
|
||||
*/
|
||||
static int send_cap_msg(struct cap_msg_args *arg)
|
||||
#define CAP_MSG_SIZE (sizeof(struct ceph_mds_caps) + \
|
||||
4 + 8 + 4 + 4 + 8 + 4 + 4 + 4 + 8 + 8 + 4)
|
||||
|
||||
/* Marshal up the cap msg to the MDS */
|
||||
static void encode_cap_msg(struct ceph_msg *msg, struct cap_msg_args *arg)
|
||||
{
|
||||
struct ceph_mds_caps *fc;
|
||||
struct ceph_msg *msg;
|
||||
void *p;
|
||||
size_t extra_len;
|
||||
struct ceph_osd_client *osdc = &arg->session->s_mdsc->fsc->client->osdc;
|
||||
|
||||
dout("send_cap_msg %s %llx %llx caps %s wanted %s dirty %s"
|
||||
" seq %u/%u tid %llu/%llu mseq %u follows %lld size %llu/%llu"
|
||||
" xattr_ver %llu xattr_len %d\n", ceph_cap_op_name(arg->op),
|
||||
arg->cid, arg->ino, ceph_cap_string(arg->caps),
|
||||
ceph_cap_string(arg->wanted), ceph_cap_string(arg->dirty),
|
||||
arg->seq, arg->issue_seq, arg->flush_tid, arg->oldest_flush_tid,
|
||||
arg->mseq, arg->follows, arg->size, arg->max_size,
|
||||
arg->xattr_version,
|
||||
dout("%s %s %llx %llx caps %s wanted %s dirty %s seq %u/%u tid %llu/%llu mseq %u follows %lld size %llu/%llu xattr_ver %llu xattr_len %d\n",
|
||||
__func__, ceph_cap_op_name(arg->op), arg->cid, arg->ino,
|
||||
ceph_cap_string(arg->caps), ceph_cap_string(arg->wanted),
|
||||
ceph_cap_string(arg->dirty), arg->seq, arg->issue_seq,
|
||||
arg->flush_tid, arg->oldest_flush_tid, arg->mseq, arg->follows,
|
||||
arg->size, arg->max_size, arg->xattr_version,
|
||||
arg->xattr_buf ? (int)arg->xattr_buf->vec.iov_len : 0);
|
||||
|
||||
/* flock buffer size + inline version + inline data size +
|
||||
* osd_epoch_barrier + oldest_flush_tid */
|
||||
extra_len = 4 + 8 + 4 + 4 + 8 + 4 + 4 + 4 + 8 + 8 + 4;
|
||||
msg = ceph_msg_new(CEPH_MSG_CLIENT_CAPS, sizeof(*fc) + extra_len,
|
||||
GFP_NOFS, false);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
msg->hdr.version = cpu_to_le16(10);
|
||||
msg->hdr.tid = cpu_to_le64(arg->flush_tid);
|
||||
|
||||
@@ -1323,9 +1314,6 @@ static int send_cap_msg(struct cap_msg_args *arg)
|
||||
|
||||
/* Advisory flags (version 10) */
|
||||
ceph_encode_32(&p, arg->flags);
|
||||
|
||||
ceph_con_send(&arg->session->s_con, msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1454,25 +1442,25 @@ static void __prep_cap(struct cap_msg_args *arg, struct ceph_cap *cap,
|
||||
*
|
||||
* Caller should hold snap_rwsem (read), s_mutex.
|
||||
*/
|
||||
static void __send_cap(struct ceph_mds_client *mdsc, struct cap_msg_args *arg,
|
||||
struct ceph_inode_info *ci)
|
||||
static void __send_cap(struct cap_msg_args *arg, struct ceph_inode_info *ci)
|
||||
{
|
||||
struct ceph_msg *msg;
|
||||
struct inode *inode = &ci->vfs_inode;
|
||||
int ret;
|
||||
|
||||
ret = send_cap_msg(arg);
|
||||
if (ret < 0) {
|
||||
pr_err("error sending cap msg, ino (%llx.%llx) "
|
||||
"flushing %s tid %llu, requeue\n",
|
||||
msg = ceph_msg_new(CEPH_MSG_CLIENT_CAPS, CAP_MSG_SIZE, GFP_NOFS, false);
|
||||
if (!msg) {
|
||||
pr_err("error allocating cap msg: ino (%llx.%llx) flushing %s tid %llu, requeuing cap.\n",
|
||||
ceph_vinop(inode), ceph_cap_string(arg->dirty),
|
||||
arg->flush_tid);
|
||||
spin_lock(&ci->i_ceph_lock);
|
||||
__cap_delay_requeue(mdsc, ci);
|
||||
__cap_delay_requeue(arg->session->s_mdsc, ci);
|
||||
spin_unlock(&ci->i_ceph_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
encode_cap_msg(msg, arg);
|
||||
ceph_con_send(&arg->session->s_con, msg);
|
||||
ceph_buffer_put(arg->old_xattr_buf);
|
||||
|
||||
if (arg->wake)
|
||||
wake_up_all(&ci->i_cap_wq);
|
||||
}
|
||||
@@ -1483,6 +1471,11 @@ static inline int __send_flush_snap(struct inode *inode,
|
||||
u32 mseq, u64 oldest_flush_tid)
|
||||
{
|
||||
struct cap_msg_args arg;
|
||||
struct ceph_msg *msg;
|
||||
|
||||
msg = ceph_msg_new(CEPH_MSG_CLIENT_CAPS, CAP_MSG_SIZE, GFP_NOFS, false);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
arg.session = session;
|
||||
arg.ino = ceph_vino(inode).ino;
|
||||
@@ -1521,7 +1514,9 @@ static inline int __send_flush_snap(struct inode *inode,
|
||||
arg.flags = 0;
|
||||
arg.wake = false;
|
||||
|
||||
return send_cap_msg(&arg);
|
||||
encode_cap_msg(msg, &arg);
|
||||
ceph_con_send(&arg.session->s_con, msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1906,9 +1901,8 @@ bool __ceph_should_report_size(struct ceph_inode_info *ci)
|
||||
void ceph_check_caps(struct ceph_inode_info *ci, int flags,
|
||||
struct ceph_mds_session *session)
|
||||
{
|
||||
struct ceph_fs_client *fsc = ceph_inode_to_client(&ci->vfs_inode);
|
||||
struct ceph_mds_client *mdsc = fsc->mdsc;
|
||||
struct inode *inode = &ci->vfs_inode;
|
||||
struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(inode->i_sb);
|
||||
struct ceph_cap *cap;
|
||||
u64 flush_tid, oldest_flush_tid;
|
||||
int file_wanted, used, cap_used;
|
||||
@@ -1928,12 +1922,24 @@ void ceph_check_caps(struct ceph_inode_info *ci, int flags,
|
||||
retry:
|
||||
spin_lock(&ci->i_ceph_lock);
|
||||
retry_locked:
|
||||
/* Caps wanted by virtue of active open files. */
|
||||
file_wanted = __ceph_caps_file_wanted(ci);
|
||||
|
||||
/* Caps which have active references against them */
|
||||
used = __ceph_caps_used(ci);
|
||||
|
||||
/*
|
||||
* "issued" represents the current caps that the MDS wants us to have.
|
||||
* "implemented" is the set that we have been granted, and includes the
|
||||
* ones that have not yet been returned to the MDS (the "revoking" set,
|
||||
* usually because they have outstanding references).
|
||||
*/
|
||||
issued = __ceph_caps_issued(ci, &implemented);
|
||||
revoking = implemented & ~issued;
|
||||
|
||||
want = file_wanted;
|
||||
|
||||
/* The ones we currently want to retain (may be adjusted below) */
|
||||
retain = file_wanted | used | CEPH_CAP_PIN;
|
||||
if (!mdsc->stopping && inode->i_nlink > 0) {
|
||||
if (file_wanted) {
|
||||
@@ -2011,6 +2017,10 @@ retry_locked:
|
||||
|
||||
/* NOTE: no side-effects allowed, until we take s_mutex */
|
||||
|
||||
/*
|
||||
* If we have an auth cap, we don't need to consider any
|
||||
* overlapping caps as used.
|
||||
*/
|
||||
cap_used = used;
|
||||
if (ci->i_auth_cap && cap != ci->i_auth_cap)
|
||||
cap_used &= ~ci->i_auth_cap->issued;
|
||||
@@ -2148,7 +2158,7 @@ ack:
|
||||
want, retain, flushing, flush_tid, oldest_flush_tid);
|
||||
spin_unlock(&ci->i_ceph_lock);
|
||||
|
||||
__send_cap(mdsc, &arg, ci);
|
||||
__send_cap(&arg, ci);
|
||||
|
||||
goto retry; /* retake i_ceph_lock and restart our cap scan. */
|
||||
}
|
||||
@@ -2222,7 +2232,7 @@ retry_locked:
|
||||
flushing, flush_tid, oldest_flush_tid);
|
||||
spin_unlock(&ci->i_ceph_lock);
|
||||
|
||||
__send_cap(mdsc, &arg, ci);
|
||||
__send_cap(&arg, ci);
|
||||
} else {
|
||||
if (!list_empty(&ci->i_cap_flush_list)) {
|
||||
struct ceph_cap_flush *cf =
|
||||
@@ -2436,7 +2446,7 @@ static void __kick_flushing_caps(struct ceph_mds_client *mdsc,
|
||||
(cap->issued | cap->implemented),
|
||||
cf->caps, cf->tid, oldest_flush_tid);
|
||||
spin_unlock(&ci->i_ceph_lock);
|
||||
__send_cap(mdsc, &arg, ci);
|
||||
__send_cap(&arg, ci);
|
||||
} else {
|
||||
struct ceph_cap_snap *capsnap =
|
||||
container_of(cf, struct ceph_cap_snap,
|
||||
@@ -4284,13 +4294,30 @@ void __ceph_touch_fmode(struct ceph_inode_info *ci,
|
||||
|
||||
void ceph_get_fmode(struct ceph_inode_info *ci, int fmode, int count)
|
||||
{
|
||||
int i;
|
||||
struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(ci->vfs_inode.i_sb);
|
||||
int bits = (fmode << 1) | 1;
|
||||
bool is_opened = false;
|
||||
int i;
|
||||
|
||||
if (count == 1)
|
||||
atomic64_inc(&mdsc->metric.opened_files);
|
||||
|
||||
spin_lock(&ci->i_ceph_lock);
|
||||
for (i = 0; i < CEPH_FILE_MODE_BITS; i++) {
|
||||
if (bits & (1 << i))
|
||||
ci->i_nr_by_mode[i] += count;
|
||||
|
||||
/*
|
||||
* If any of the mode ref is larger than 1,
|
||||
* that means it has been already opened by
|
||||
* others. Just skip checking the PIN ref.
|
||||
*/
|
||||
if (i && ci->i_nr_by_mode[i] > 1)
|
||||
is_opened = true;
|
||||
}
|
||||
|
||||
if (!is_opened)
|
||||
percpu_counter_inc(&mdsc->metric.opened_inodes);
|
||||
spin_unlock(&ci->i_ceph_lock);
|
||||
}
|
||||
|
||||
@@ -4301,15 +4328,32 @@ void ceph_get_fmode(struct ceph_inode_info *ci, int fmode, int count)
|
||||
*/
|
||||
void ceph_put_fmode(struct ceph_inode_info *ci, int fmode, int count)
|
||||
{
|
||||
int i;
|
||||
struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(ci->vfs_inode.i_sb);
|
||||
int bits = (fmode << 1) | 1;
|
||||
bool is_closed = true;
|
||||
int i;
|
||||
|
||||
if (count == 1)
|
||||
atomic64_dec(&mdsc->metric.opened_files);
|
||||
|
||||
spin_lock(&ci->i_ceph_lock);
|
||||
for (i = 0; i < CEPH_FILE_MODE_BITS; i++) {
|
||||
if (bits & (1 << i)) {
|
||||
BUG_ON(ci->i_nr_by_mode[i] < count);
|
||||
ci->i_nr_by_mode[i] -= count;
|
||||
}
|
||||
|
||||
/*
|
||||
* If any of the mode ref is not 0 after
|
||||
* decreased, that means it is still opened
|
||||
* by others. Just skip checking the PIN ref.
|
||||
*/
|
||||
if (i && ci->i_nr_by_mode[i])
|
||||
is_closed = false;
|
||||
}
|
||||
|
||||
if (is_closed)
|
||||
percpu_counter_dec(&mdsc->metric.opened_inodes);
|
||||
spin_unlock(&ci->i_ceph_lock);
|
||||
}
|
||||
|
||||
|
@@ -148,6 +148,17 @@ static int metric_show(struct seq_file *s, void *p)
|
||||
int nr_caps = 0;
|
||||
s64 total, sum, avg, min, max, sq;
|
||||
|
||||
sum = percpu_counter_sum(&m->total_inodes);
|
||||
seq_printf(s, "item total\n");
|
||||
seq_printf(s, "------------------------------------------\n");
|
||||
seq_printf(s, "%-35s%lld / %lld\n", "opened files / total inodes",
|
||||
atomic64_read(&m->opened_files), sum);
|
||||
seq_printf(s, "%-35s%lld / %lld\n", "pinned i_caps / total inodes",
|
||||
atomic64_read(&m->total_caps), sum);
|
||||
seq_printf(s, "%-35s%lld / %lld\n", "opened inodes / total inodes",
|
||||
percpu_counter_sum(&m->opened_inodes), sum);
|
||||
|
||||
seq_printf(s, "\n");
|
||||
seq_printf(s, "item total avg_lat(us) min_lat(us) max_lat(us) stdev(us)\n");
|
||||
seq_printf(s, "-----------------------------------------------------------------------------------\n");
|
||||
|
||||
@@ -202,7 +213,8 @@ static int caps_show_cb(struct inode *inode, struct ceph_cap *cap, void *p)
|
||||
{
|
||||
struct seq_file *s = p;
|
||||
|
||||
seq_printf(s, "0x%-17llx%-17s%-17s\n", ceph_ino(inode),
|
||||
seq_printf(s, "0x%-17llx%-3d%-17s%-17s\n", ceph_ino(inode),
|
||||
cap->session->s_mds,
|
||||
ceph_cap_string(cap->issued),
|
||||
ceph_cap_string(cap->implemented));
|
||||
return 0;
|
||||
@@ -222,8 +234,8 @@ static int caps_show(struct seq_file *s, void *p)
|
||||
"reserved\t%d\n"
|
||||
"min\t\t%d\n\n",
|
||||
total, avail, used, reserved, min);
|
||||
seq_printf(s, "ino issued implemented\n");
|
||||
seq_printf(s, "-----------------------------------------------\n");
|
||||
seq_printf(s, "ino mds issued implemented\n");
|
||||
seq_printf(s, "--------------------------------------------------\n");
|
||||
|
||||
mutex_lock(&mdsc->mutex);
|
||||
for (i = 0; i < mdsc->max_sessions; i++) {
|
||||
|
@@ -38,8 +38,7 @@ static int __dir_lease_try_check(const struct dentry *dentry);
|
||||
static int ceph_d_init(struct dentry *dentry)
|
||||
{
|
||||
struct ceph_dentry_info *di;
|
||||
struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb);
|
||||
struct ceph_mds_client *mdsc = fsc->mdsc;
|
||||
struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(dentry->d_sb);
|
||||
|
||||
di = kmem_cache_zalloc(ceph_dentry_cachep, GFP_KERNEL);
|
||||
if (!di)
|
||||
@@ -738,7 +737,7 @@ static struct dentry *ceph_lookup(struct inode *dir, struct dentry *dentry,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct ceph_fs_client *fsc = ceph_sb_to_client(dir->i_sb);
|
||||
struct ceph_mds_client *mdsc = fsc->mdsc;
|
||||
struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(dir->i_sb);
|
||||
struct ceph_mds_request *req;
|
||||
int op;
|
||||
int mask;
|
||||
@@ -827,8 +826,7 @@ int ceph_handle_notrace_create(struct inode *dir, struct dentry *dentry)
|
||||
static int ceph_mknod(struct inode *dir, struct dentry *dentry,
|
||||
umode_t mode, dev_t rdev)
|
||||
{
|
||||
struct ceph_fs_client *fsc = ceph_sb_to_client(dir->i_sb);
|
||||
struct ceph_mds_client *mdsc = fsc->mdsc;
|
||||
struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(dir->i_sb);
|
||||
struct ceph_mds_request *req;
|
||||
struct ceph_acl_sec_ctx as_ctx = {};
|
||||
int err;
|
||||
@@ -889,8 +887,7 @@ static int ceph_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||
static int ceph_symlink(struct inode *dir, struct dentry *dentry,
|
||||
const char *dest)
|
||||
{
|
||||
struct ceph_fs_client *fsc = ceph_sb_to_client(dir->i_sb);
|
||||
struct ceph_mds_client *mdsc = fsc->mdsc;
|
||||
struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(dir->i_sb);
|
||||
struct ceph_mds_request *req;
|
||||
struct ceph_acl_sec_ctx as_ctx = {};
|
||||
int err;
|
||||
@@ -942,8 +939,7 @@ out:
|
||||
|
||||
static int ceph_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
{
|
||||
struct ceph_fs_client *fsc = ceph_sb_to_client(dir->i_sb);
|
||||
struct ceph_mds_client *mdsc = fsc->mdsc;
|
||||
struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(dir->i_sb);
|
||||
struct ceph_mds_request *req;
|
||||
struct ceph_acl_sec_ctx as_ctx = {};
|
||||
int err = -EROFS;
|
||||
@@ -1010,8 +1006,7 @@ out:
|
||||
static int ceph_link(struct dentry *old_dentry, struct inode *dir,
|
||||
struct dentry *dentry)
|
||||
{
|
||||
struct ceph_fs_client *fsc = ceph_sb_to_client(dir->i_sb);
|
||||
struct ceph_mds_client *mdsc = fsc->mdsc;
|
||||
struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(dir->i_sb);
|
||||
struct ceph_mds_request *req;
|
||||
int err;
|
||||
|
||||
@@ -1192,8 +1187,7 @@ static int ceph_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
struct inode *new_dir, struct dentry *new_dentry,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct ceph_fs_client *fsc = ceph_sb_to_client(old_dir->i_sb);
|
||||
struct ceph_mds_client *mdsc = fsc->mdsc;
|
||||
struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(old_dir->i_sb);
|
||||
struct ceph_mds_request *req;
|
||||
int op = CEPH_MDS_OP_RENAME;
|
||||
int err;
|
||||
|
@@ -182,8 +182,7 @@ static void put_bvecs(struct bio_vec *bvecs, int num_bvecs, bool should_dirty)
|
||||
static struct ceph_mds_request *
|
||||
prepare_open_request(struct super_block *sb, int flags, int create_mode)
|
||||
{
|
||||
struct ceph_fs_client *fsc = ceph_sb_to_client(sb);
|
||||
struct ceph_mds_client *mdsc = fsc->mdsc;
|
||||
struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(sb);
|
||||
struct ceph_mds_request *req;
|
||||
int want_auth = USE_ANY_MDS;
|
||||
int op = (flags & O_CREAT) ? CEPH_MDS_OP_CREATE : CEPH_MDS_OP_OPEN;
|
||||
@@ -256,8 +255,6 @@ static int ceph_init_file(struct inode *inode, struct file *file, int fmode)
|
||||
case S_IFDIR:
|
||||
ret = ceph_init_file_info(inode, file, fmode,
|
||||
S_ISDIR(inode->i_mode));
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
|
||||
case S_IFLNK:
|
||||
@@ -285,7 +282,7 @@ static int ceph_init_file(struct inode *inode, struct file *file, int fmode)
|
||||
*/
|
||||
int ceph_renew_caps(struct inode *inode, int fmode)
|
||||
{
|
||||
struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc;
|
||||
struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(inode->i_sb);
|
||||
struct ceph_inode_info *ci = ceph_inode(inode);
|
||||
struct ceph_mds_request *req;
|
||||
int err, flags, wanted;
|
||||
@@ -865,6 +862,8 @@ static ssize_t ceph_sync_read(struct kiocb *iocb, struct iov_iter *to,
|
||||
size_t page_off;
|
||||
u64 i_size;
|
||||
bool more;
|
||||
int idx;
|
||||
size_t left;
|
||||
|
||||
req = ceph_osdc_new_request(osdc, &ci->i_layout,
|
||||
ci->i_vino, off, &len, 0, 1,
|
||||
@@ -878,29 +877,13 @@ static ssize_t ceph_sync_read(struct kiocb *iocb, struct iov_iter *to,
|
||||
|
||||
more = len < iov_iter_count(to);
|
||||
|
||||
if (unlikely(iov_iter_is_pipe(to))) {
|
||||
ret = iov_iter_get_pages_alloc(to, &pages, len,
|
||||
&page_off);
|
||||
if (ret <= 0) {
|
||||
ceph_osdc_put_request(req);
|
||||
ret = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
num_pages = DIV_ROUND_UP(ret + page_off, PAGE_SIZE);
|
||||
if (ret < len) {
|
||||
len = ret;
|
||||
osd_req_op_extent_update(req, 0, len);
|
||||
more = false;
|
||||
}
|
||||
} else {
|
||||
num_pages = calc_pages_for(off, len);
|
||||
page_off = off & ~PAGE_MASK;
|
||||
pages = ceph_alloc_page_vector(num_pages, GFP_KERNEL);
|
||||
if (IS_ERR(pages)) {
|
||||
ceph_osdc_put_request(req);
|
||||
ret = PTR_ERR(pages);
|
||||
break;
|
||||
}
|
||||
num_pages = calc_pages_for(off, len);
|
||||
page_off = off & ~PAGE_MASK;
|
||||
pages = ceph_alloc_page_vector(num_pages, GFP_KERNEL);
|
||||
if (IS_ERR(pages)) {
|
||||
ceph_osdc_put_request(req);
|
||||
ret = PTR_ERR(pages);
|
||||
break;
|
||||
}
|
||||
|
||||
osd_req_op_extent_osd_data_pages(req, 0, pages, len, page_off,
|
||||
@@ -931,36 +914,27 @@ static ssize_t ceph_sync_read(struct kiocb *iocb, struct iov_iter *to,
|
||||
ret += zlen;
|
||||
}
|
||||
|
||||
if (unlikely(iov_iter_is_pipe(to))) {
|
||||
if (ret > 0) {
|
||||
iov_iter_advance(to, ret);
|
||||
off += ret;
|
||||
} else {
|
||||
iov_iter_advance(to, 0);
|
||||
idx = 0;
|
||||
left = ret > 0 ? ret : 0;
|
||||
while (left > 0) {
|
||||
size_t len, copied;
|
||||
page_off = off & ~PAGE_MASK;
|
||||
len = min_t(size_t, left, PAGE_SIZE - page_off);
|
||||
SetPageUptodate(pages[idx]);
|
||||
copied = copy_page_to_iter(pages[idx++],
|
||||
page_off, len, to);
|
||||
off += copied;
|
||||
left -= copied;
|
||||
if (copied < len) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
ceph_put_page_vector(pages, num_pages, false);
|
||||
} else {
|
||||
int idx = 0;
|
||||
size_t left = ret > 0 ? ret : 0;
|
||||
while (left > 0) {
|
||||
size_t len, copied;
|
||||
page_off = off & ~PAGE_MASK;
|
||||
len = min_t(size_t, left, PAGE_SIZE - page_off);
|
||||
copied = copy_page_to_iter(pages[idx++],
|
||||
page_off, len, to);
|
||||
off += copied;
|
||||
left -= copied;
|
||||
if (copied < len) {
|
||||
ret = -EFAULT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ceph_release_page_vector(pages, num_pages);
|
||||
}
|
||||
ceph_release_page_vector(pages, num_pages);
|
||||
|
||||
if (ret < 0) {
|
||||
if (ret == -EBLACKLISTED)
|
||||
fsc->blacklisted = true;
|
||||
if (ret == -EBLOCKLISTED)
|
||||
fsc->blocklisted = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1052,8 +1026,7 @@ static void ceph_aio_complete_req(struct ceph_osd_request *req)
|
||||
struct inode *inode = req->r_inode;
|
||||
struct ceph_aio_request *aio_req = req->r_priv;
|
||||
struct ceph_osd_data *osd_data = osd_req_op_extent_osd_data(req, 0);
|
||||
struct ceph_fs_client *fsc = ceph_inode_to_client(inode);
|
||||
struct ceph_client_metric *metric = &fsc->mdsc->metric;
|
||||
struct ceph_client_metric *metric = &ceph_sb_to_mdsc(inode->i_sb)->metric;
|
||||
|
||||
BUG_ON(osd_data->type != CEPH_OSD_DATA_TYPE_BVECS);
|
||||
BUG_ON(!osd_data->num_bvecs);
|
||||
|
@@ -42,10 +42,13 @@ static void ceph_inode_work(struct work_struct *work);
|
||||
static int ceph_set_ino_cb(struct inode *inode, void *data)
|
||||
{
|
||||
struct ceph_inode_info *ci = ceph_inode(inode);
|
||||
struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(inode->i_sb);
|
||||
|
||||
ci->i_vino = *(struct ceph_vino *)data;
|
||||
inode->i_ino = ceph_vino_to_ino_t(ci->i_vino);
|
||||
inode_set_iversion_raw(inode, 0);
|
||||
percpu_counter_inc(&mdsc->metric.total_inodes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -538,11 +541,14 @@ void ceph_free_inode(struct inode *inode)
|
||||
void ceph_evict_inode(struct inode *inode)
|
||||
{
|
||||
struct ceph_inode_info *ci = ceph_inode(inode);
|
||||
struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(inode->i_sb);
|
||||
struct ceph_inode_frag *frag;
|
||||
struct rb_node *n;
|
||||
|
||||
dout("evict_inode %p ino %llx.%llx\n", inode, ceph_vinop(inode));
|
||||
|
||||
percpu_counter_dec(&mdsc->metric.total_inodes);
|
||||
|
||||
truncate_inode_pages_final(&inode->i_data);
|
||||
clear_inode(inode);
|
||||
|
||||
@@ -558,8 +564,6 @@ void ceph_evict_inode(struct inode *inode)
|
||||
* caps in i_snap_caps.
|
||||
*/
|
||||
if (ci->i_snap_realm) {
|
||||
struct ceph_mds_client *mdsc =
|
||||
ceph_inode_to_client(inode)->mdsc;
|
||||
if (ceph_snap(inode) == CEPH_NOSNAP) {
|
||||
struct ceph_snap_realm *realm = ci->i_snap_realm;
|
||||
dout(" dropping residual ref to snap realm %p\n",
|
||||
@@ -739,7 +743,7 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page,
|
||||
struct ceph_mds_session *session, int cap_fmode,
|
||||
struct ceph_cap_reservation *caps_reservation)
|
||||
{
|
||||
struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc;
|
||||
struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(inode->i_sb);
|
||||
struct ceph_mds_reply_inode *info = iinfo->in;
|
||||
struct ceph_inode_info *ci = ceph_inode(inode);
|
||||
int issued, new_issued, info_caps;
|
||||
|
@@ -63,7 +63,7 @@ static const struct file_lock_operations ceph_fl_lock_ops = {
|
||||
static int ceph_lock_message(u8 lock_type, u16 operation, struct inode *inode,
|
||||
int cmd, u8 wait, struct file_lock *fl)
|
||||
{
|
||||
struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc;
|
||||
struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(inode->i_sb);
|
||||
struct ceph_mds_request *req;
|
||||
int err;
|
||||
u64 length = 0;
|
||||
|
@@ -3303,7 +3303,7 @@ bad:
|
||||
}
|
||||
|
||||
static int __decode_session_metadata(void **p, void *end,
|
||||
bool *blacklisted)
|
||||
bool *blocklisted)
|
||||
{
|
||||
/* map<string,string> */
|
||||
u32 n;
|
||||
@@ -3317,8 +3317,12 @@ static int __decode_session_metadata(void **p, void *end,
|
||||
*p += len;
|
||||
ceph_decode_32_safe(p, end, len, bad);
|
||||
ceph_decode_need(p, end, len, bad);
|
||||
/*
|
||||
* Match "blocklisted (blacklisted)" from newer MDSes,
|
||||
* or "blacklisted" from older MDSes.
|
||||
*/
|
||||
if (err_str && strnstr(*p, "blacklisted", len))
|
||||
*blacklisted = true;
|
||||
*blocklisted = true;
|
||||
*p += len;
|
||||
}
|
||||
return 0;
|
||||
@@ -3341,7 +3345,7 @@ static void handle_session(struct ceph_mds_session *session,
|
||||
u32 op;
|
||||
u64 seq, features = 0;
|
||||
int wake = 0;
|
||||
bool blacklisted = false;
|
||||
bool blocklisted = false;
|
||||
|
||||
/* decode */
|
||||
ceph_decode_need(&p, end, sizeof(*h), bad);
|
||||
@@ -3354,7 +3358,7 @@ static void handle_session(struct ceph_mds_session *session,
|
||||
if (msg_version >= 3) {
|
||||
u32 len;
|
||||
/* version >= 2, metadata */
|
||||
if (__decode_session_metadata(&p, end, &blacklisted) < 0)
|
||||
if (__decode_session_metadata(&p, end, &blocklisted) < 0)
|
||||
goto bad;
|
||||
/* version >= 3, feature bits */
|
||||
ceph_decode_32_safe(&p, end, len, bad);
|
||||
@@ -3445,8 +3449,8 @@ static void handle_session(struct ceph_mds_session *session,
|
||||
session->s_state = CEPH_MDS_SESSION_REJECTED;
|
||||
cleanup_session_requests(mdsc, session);
|
||||
remove_session_caps(session);
|
||||
if (blacklisted)
|
||||
mdsc->fsc->blacklisted = true;
|
||||
if (blocklisted)
|
||||
mdsc->fsc->blocklisted = true;
|
||||
wake = 2; /* for good measure */
|
||||
break;
|
||||
|
||||
@@ -3612,6 +3616,39 @@ fail_msg:
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct dentry* d_find_primary(struct inode *inode)
|
||||
{
|
||||
struct dentry *alias, *dn = NULL;
|
||||
|
||||
if (hlist_empty(&inode->i_dentry))
|
||||
return NULL;
|
||||
|
||||
spin_lock(&inode->i_lock);
|
||||
if (hlist_empty(&inode->i_dentry))
|
||||
goto out_unlock;
|
||||
|
||||
if (S_ISDIR(inode->i_mode)) {
|
||||
alias = hlist_entry(inode->i_dentry.first, struct dentry, d_u.d_alias);
|
||||
if (!IS_ROOT(alias))
|
||||
dn = dget(alias);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) {
|
||||
spin_lock(&alias->d_lock);
|
||||
if (!d_unhashed(alias) &&
|
||||
(ceph_dentry(alias)->flags & CEPH_DENTRY_PRIMARY_LINK)) {
|
||||
dn = dget_dlock(alias);
|
||||
}
|
||||
spin_unlock(&alias->d_lock);
|
||||
if (dn)
|
||||
break;
|
||||
}
|
||||
out_unlock:
|
||||
spin_unlock(&inode->i_lock);
|
||||
return dn;
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode information about a cap for a reconnect with the MDS.
|
||||
*/
|
||||
@@ -3625,13 +3662,32 @@ static int reconnect_caps_cb(struct inode *inode, struct ceph_cap *cap,
|
||||
struct ceph_inode_info *ci = cap->ci;
|
||||
struct ceph_reconnect_state *recon_state = arg;
|
||||
struct ceph_pagelist *pagelist = recon_state->pagelist;
|
||||
int err;
|
||||
struct dentry *dentry;
|
||||
char *path;
|
||||
int pathlen, err;
|
||||
u64 pathbase;
|
||||
u64 snap_follows;
|
||||
|
||||
dout(" adding %p ino %llx.%llx cap %p %lld %s\n",
|
||||
inode, ceph_vinop(inode), cap, cap->cap_id,
|
||||
ceph_cap_string(cap->issued));
|
||||
|
||||
dentry = d_find_primary(inode);
|
||||
if (dentry) {
|
||||
/* set pathbase to parent dir when msg_version >= 2 */
|
||||
path = ceph_mdsc_build_path(dentry, &pathlen, &pathbase,
|
||||
recon_state->msg_version >= 2);
|
||||
dput(dentry);
|
||||
if (IS_ERR(path)) {
|
||||
err = PTR_ERR(path);
|
||||
goto out_err;
|
||||
}
|
||||
} else {
|
||||
path = NULL;
|
||||
pathlen = 0;
|
||||
pathbase = 0;
|
||||
}
|
||||
|
||||
spin_lock(&ci->i_ceph_lock);
|
||||
cap->seq = 0; /* reset cap seq */
|
||||
cap->issue_seq = 0; /* and issue_seq */
|
||||
@@ -3652,7 +3708,7 @@ static int reconnect_caps_cb(struct inode *inode, struct ceph_cap *cap,
|
||||
rec.v2.wanted = cpu_to_le32(__ceph_caps_wanted(ci));
|
||||
rec.v2.issued = cpu_to_le32(cap->issued);
|
||||
rec.v2.snaprealm = cpu_to_le64(ci->i_snap_realm->ino);
|
||||
rec.v2.pathbase = 0;
|
||||
rec.v2.pathbase = cpu_to_le64(pathbase);
|
||||
rec.v2.flock_len = (__force __le32)
|
||||
((ci->i_ceph_flags & CEPH_I_ERROR_FILELOCK) ? 0 : 1);
|
||||
} else {
|
||||
@@ -3663,7 +3719,7 @@ static int reconnect_caps_cb(struct inode *inode, struct ceph_cap *cap,
|
||||
ceph_encode_timespec64(&rec.v1.mtime, &inode->i_mtime);
|
||||
ceph_encode_timespec64(&rec.v1.atime, &inode->i_atime);
|
||||
rec.v1.snaprealm = cpu_to_le64(ci->i_snap_realm->ino);
|
||||
rec.v1.pathbase = 0;
|
||||
rec.v1.pathbase = cpu_to_le64(pathbase);
|
||||
}
|
||||
|
||||
if (list_empty(&ci->i_cap_snaps)) {
|
||||
@@ -3725,7 +3781,7 @@ encode_again:
|
||||
sizeof(struct ceph_filelock);
|
||||
rec.v2.flock_len = cpu_to_le32(struct_len);
|
||||
|
||||
struct_len += sizeof(u32) + sizeof(rec.v2);
|
||||
struct_len += sizeof(u32) + pathlen + sizeof(rec.v2);
|
||||
|
||||
if (struct_v >= 2)
|
||||
struct_len += sizeof(u64); /* snap_follows */
|
||||
@@ -3749,7 +3805,7 @@ encode_again:
|
||||
ceph_pagelist_encode_8(pagelist, 1);
|
||||
ceph_pagelist_encode_32(pagelist, struct_len);
|
||||
}
|
||||
ceph_pagelist_encode_string(pagelist, NULL, 0);
|
||||
ceph_pagelist_encode_string(pagelist, path, pathlen);
|
||||
ceph_pagelist_append(pagelist, &rec, sizeof(rec.v2));
|
||||
ceph_locks_to_pagelist(flocks, pagelist,
|
||||
num_fcntl_locks, num_flock_locks);
|
||||
@@ -3758,39 +3814,20 @@ encode_again:
|
||||
out_freeflocks:
|
||||
kfree(flocks);
|
||||
} else {
|
||||
u64 pathbase = 0;
|
||||
int pathlen = 0;
|
||||
char *path = NULL;
|
||||
struct dentry *dentry;
|
||||
|
||||
dentry = d_find_alias(inode);
|
||||
if (dentry) {
|
||||
path = ceph_mdsc_build_path(dentry,
|
||||
&pathlen, &pathbase, 0);
|
||||
dput(dentry);
|
||||
if (IS_ERR(path)) {
|
||||
err = PTR_ERR(path);
|
||||
goto out_err;
|
||||
}
|
||||
rec.v1.pathbase = cpu_to_le64(pathbase);
|
||||
}
|
||||
|
||||
err = ceph_pagelist_reserve(pagelist,
|
||||
sizeof(u64) + sizeof(u32) +
|
||||
pathlen + sizeof(rec.v1));
|
||||
if (err) {
|
||||
goto out_freepath;
|
||||
}
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
ceph_pagelist_encode_64(pagelist, ceph_ino(inode));
|
||||
ceph_pagelist_encode_string(pagelist, path, pathlen);
|
||||
ceph_pagelist_append(pagelist, &rec, sizeof(rec.v1));
|
||||
out_freepath:
|
||||
ceph_mdsc_free_path(path, pathlen);
|
||||
}
|
||||
|
||||
out_err:
|
||||
if (err >= 0)
|
||||
ceph_mdsc_free_path(path, pathlen);
|
||||
if (!err)
|
||||
recon_state->nr_caps++;
|
||||
return err;
|
||||
}
|
||||
@@ -4334,14 +4371,14 @@ static void maybe_recover_session(struct ceph_mds_client *mdsc)
|
||||
if (READ_ONCE(fsc->mount_state) != CEPH_MOUNT_MOUNTED)
|
||||
return;
|
||||
|
||||
if (!READ_ONCE(fsc->blacklisted))
|
||||
if (!READ_ONCE(fsc->blocklisted))
|
||||
return;
|
||||
|
||||
if (fsc->last_auto_reconnect &&
|
||||
time_before(jiffies, fsc->last_auto_reconnect + HZ * 60 * 30))
|
||||
return;
|
||||
|
||||
pr_info("auto reconnect after blacklisted\n");
|
||||
pr_info("auto reconnect after blocklisted\n");
|
||||
fsc->last_auto_reconnect = jiffies;
|
||||
ceph_force_reconnect(fsc->sb);
|
||||
}
|
||||
|
@@ -393,7 +393,7 @@ struct ceph_mds_client {
|
||||
|
||||
struct ceph_mds_session **sessions; /* NULL for mds if no session */
|
||||
atomic_t num_sessions;
|
||||
int max_sessions; /* len of s_mds_sessions */
|
||||
int max_sessions; /* len of sessions array */
|
||||
int stopping; /* true if shutting down */
|
||||
|
||||
atomic64_t quotarealms_count; /* # realms with quota */
|
||||
|
@@ -192,11 +192,23 @@ int ceph_metric_init(struct ceph_client_metric *m)
|
||||
m->total_metadatas = 0;
|
||||
m->metadata_latency_sum = 0;
|
||||
|
||||
atomic64_set(&m->opened_files, 0);
|
||||
ret = percpu_counter_init(&m->opened_inodes, 0, GFP_KERNEL);
|
||||
if (ret)
|
||||
goto err_opened_inodes;
|
||||
ret = percpu_counter_init(&m->total_inodes, 0, GFP_KERNEL);
|
||||
if (ret)
|
||||
goto err_total_inodes;
|
||||
|
||||
m->session = NULL;
|
||||
INIT_DELAYED_WORK(&m->delayed_work, metric_delayed_work);
|
||||
|
||||
return 0;
|
||||
|
||||
err_total_inodes:
|
||||
percpu_counter_destroy(&m->opened_inodes);
|
||||
err_opened_inodes:
|
||||
percpu_counter_destroy(&m->i_caps_mis);
|
||||
err_i_caps_mis:
|
||||
percpu_counter_destroy(&m->i_caps_hit);
|
||||
err_i_caps_hit:
|
||||
@@ -212,6 +224,8 @@ void ceph_metric_destroy(struct ceph_client_metric *m)
|
||||
if (!m)
|
||||
return;
|
||||
|
||||
percpu_counter_destroy(&m->total_inodes);
|
||||
percpu_counter_destroy(&m->opened_inodes);
|
||||
percpu_counter_destroy(&m->i_caps_mis);
|
||||
percpu_counter_destroy(&m->i_caps_hit);
|
||||
percpu_counter_destroy(&m->d_lease_mis);
|
||||
|
@@ -115,6 +115,13 @@ struct ceph_client_metric {
|
||||
ktime_t metadata_latency_min;
|
||||
ktime_t metadata_latency_max;
|
||||
|
||||
/* The total number of directories and files that are opened */
|
||||
atomic64_t opened_files;
|
||||
|
||||
/* The total number of inodes that have opened files or directories */
|
||||
struct percpu_counter opened_inodes;
|
||||
struct percpu_counter total_inodes;
|
||||
|
||||
struct ceph_mds_session *session;
|
||||
struct delayed_work delayed_work; /* delayed work */
|
||||
};
|
||||
|
@@ -12,7 +12,7 @@
|
||||
|
||||
void ceph_adjust_quota_realms_count(struct inode *inode, bool inc)
|
||||
{
|
||||
struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc;
|
||||
struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(inode->i_sb);
|
||||
if (inc)
|
||||
atomic64_inc(&mdsc->quotarealms_count);
|
||||
else
|
||||
@@ -21,8 +21,8 @@ void ceph_adjust_quota_realms_count(struct inode *inode, bool inc)
|
||||
|
||||
static inline bool ceph_has_realms_with_quotas(struct inode *inode)
|
||||
{
|
||||
struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc;
|
||||
struct super_block *sb = mdsc->fsc->sb;
|
||||
struct super_block *sb = inode->i_sb;
|
||||
struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(sb);
|
||||
struct inode *root = d_inode(sb->s_root);
|
||||
|
||||
if (atomic64_read(&mdsc->quotarealms_count) > 0)
|
||||
@@ -266,7 +266,7 @@ restart:
|
||||
|
||||
static bool ceph_quota_is_same_realm(struct inode *old, struct inode *new)
|
||||
{
|
||||
struct ceph_mds_client *mdsc = ceph_inode_to_client(old)->mdsc;
|
||||
struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(old->i_sb);
|
||||
struct ceph_snap_realm *old_realm, *new_realm;
|
||||
bool is_same;
|
||||
|
||||
@@ -313,7 +313,7 @@ enum quota_check_op {
|
||||
static bool check_quota_exceeded(struct inode *inode, enum quota_check_op op,
|
||||
loff_t delta)
|
||||
{
|
||||
struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc;
|
||||
struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(inode->i_sb);
|
||||
struct ceph_inode_info *ci;
|
||||
struct ceph_snap_realm *realm, *next;
|
||||
struct inode *in;
|
||||
|
@@ -602,7 +602,7 @@ int __ceph_finish_cap_snap(struct ceph_inode_info *ci,
|
||||
struct ceph_cap_snap *capsnap)
|
||||
{
|
||||
struct inode *inode = &ci->vfs_inode;
|
||||
struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc;
|
||||
struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(inode->i_sb);
|
||||
|
||||
BUG_ON(capsnap->writing);
|
||||
capsnap->size = inode->i_size;
|
||||
|
@@ -1205,14 +1205,13 @@ nomem:
|
||||
static void ceph_kill_sb(struct super_block *s)
|
||||
{
|
||||
struct ceph_fs_client *fsc = ceph_sb_to_client(s);
|
||||
dev_t dev = s->s_dev;
|
||||
|
||||
dout("kill_sb %p\n", s);
|
||||
|
||||
ceph_mdsc_pre_umount(fsc->mdsc);
|
||||
flush_fs_workqueues(fsc);
|
||||
|
||||
generic_shutdown_super(s);
|
||||
kill_anon_super(s);
|
||||
|
||||
fsc->client->extra_mon_dispatch = NULL;
|
||||
ceph_fs_debugfs_cleanup(fsc);
|
||||
@@ -1220,7 +1219,6 @@ static void ceph_kill_sb(struct super_block *s)
|
||||
ceph_fscache_unregister_fs(fsc);
|
||||
|
||||
destroy_fs_client(fsc);
|
||||
free_anon_bdev(dev);
|
||||
}
|
||||
|
||||
static struct file_system_type ceph_fs_type = {
|
||||
@@ -1243,13 +1241,13 @@ int ceph_force_reconnect(struct super_block *sb)
|
||||
* see remove_session_caps_cb() */
|
||||
flush_workqueue(fsc->inode_wq);
|
||||
|
||||
/* In case that we were blacklisted. This also reset
|
||||
/* In case that we were blocklisted. This also reset
|
||||
* all mon/osd connections */
|
||||
ceph_reset_client_addr(fsc->client);
|
||||
|
||||
ceph_osdc_clear_abort_err(&fsc->client->osdc);
|
||||
|
||||
fsc->blacklisted = false;
|
||||
fsc->blocklisted = false;
|
||||
fsc->mount_state = CEPH_MOUNT_MOUNTED;
|
||||
|
||||
if (sb->s_root) {
|
||||
|
@@ -32,7 +32,7 @@
|
||||
#define CEPH_BLOCK_SHIFT 22 /* 4 MB */
|
||||
#define CEPH_BLOCK (1 << CEPH_BLOCK_SHIFT)
|
||||
|
||||
#define CEPH_MOUNT_OPT_CLEANRECOVER (1<<1) /* auto reonnect (clean mode) after blacklisted */
|
||||
#define CEPH_MOUNT_OPT_CLEANRECOVER (1<<1) /* auto reonnect (clean mode) after blocklisted */
|
||||
#define CEPH_MOUNT_OPT_DIRSTAT (1<<4) /* `cat dirname` for stats */
|
||||
#define CEPH_MOUNT_OPT_RBYTES (1<<5) /* dir st_bytes = rbytes */
|
||||
#define CEPH_MOUNT_OPT_NOASYNCREADDIR (1<<7) /* no dcache readdir */
|
||||
@@ -109,7 +109,7 @@ struct ceph_fs_client {
|
||||
unsigned long mount_state;
|
||||
|
||||
unsigned long last_auto_reconnect;
|
||||
bool blacklisted;
|
||||
bool blocklisted;
|
||||
|
||||
bool have_copy_from2;
|
||||
|
||||
@@ -160,7 +160,8 @@ struct ceph_cap {
|
||||
int issued; /* latest, from the mds */
|
||||
int implemented; /* implemented superset of
|
||||
issued (for revocation) */
|
||||
int mds, mds_wanted;
|
||||
int mds; /* mds index for this cap */
|
||||
int mds_wanted; /* caps wanted from this mds */
|
||||
};
|
||||
/* caps to release */
|
||||
struct {
|
||||
@@ -451,6 +452,12 @@ ceph_sb_to_client(const struct super_block *sb)
|
||||
return (struct ceph_fs_client *)sb->s_fs_info;
|
||||
}
|
||||
|
||||
static inline struct ceph_mds_client *
|
||||
ceph_sb_to_mdsc(const struct super_block *sb)
|
||||
{
|
||||
return (struct ceph_mds_client *)ceph_sb_to_client(sb)->mdsc;
|
||||
}
|
||||
|
||||
static inline struct ceph_vino
|
||||
ceph_vino(const struct inode *inode)
|
||||
{
|
||||
|
@@ -116,7 +116,8 @@ static ssize_t ceph_vxattrcb_layout(struct ceph_inode_info *ci, char *val,
|
||||
* NULL terminates however, so call it on a temporary buffer and then memcpy
|
||||
* the result into place.
|
||||
*/
|
||||
static int ceph_fmt_xattr(char *val, size_t size, const char *fmt, ...)
|
||||
static __printf(3, 4)
|
||||
int ceph_fmt_xattr(char *val, size_t size, const char *fmt, ...)
|
||||
{
|
||||
int ret;
|
||||
va_list args;
|
||||
|
51
fs/io-wq.c
51
fs/io-wq.c
@@ -18,6 +18,7 @@
|
||||
#include <linux/fs_struct.h>
|
||||
#include <linux/task_work.h>
|
||||
#include <linux/blk-cgroup.h>
|
||||
#include <linux/audit.h>
|
||||
|
||||
#include "io-wq.h"
|
||||
|
||||
@@ -429,14 +430,10 @@ static void io_wq_switch_mm(struct io_worker *worker, struct io_wq_work *work)
|
||||
mmput(worker->mm);
|
||||
worker->mm = NULL;
|
||||
}
|
||||
if (!work->mm)
|
||||
return;
|
||||
|
||||
if (mmget_not_zero(work->mm)) {
|
||||
kthread_use_mm(work->mm);
|
||||
worker->mm = work->mm;
|
||||
/* hang on to this mm */
|
||||
work->mm = NULL;
|
||||
if (mmget_not_zero(work->identity->mm)) {
|
||||
kthread_use_mm(work->identity->mm);
|
||||
worker->mm = work->identity->mm;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -448,9 +445,11 @@ static inline void io_wq_switch_blkcg(struct io_worker *worker,
|
||||
struct io_wq_work *work)
|
||||
{
|
||||
#ifdef CONFIG_BLK_CGROUP
|
||||
if (work->blkcg_css != worker->blkcg_css) {
|
||||
kthread_associate_blkcg(work->blkcg_css);
|
||||
worker->blkcg_css = work->blkcg_css;
|
||||
if (!(work->flags & IO_WQ_WORK_BLKCG))
|
||||
return;
|
||||
if (work->identity->blkcg_css != worker->blkcg_css) {
|
||||
kthread_associate_blkcg(work->identity->blkcg_css);
|
||||
worker->blkcg_css = work->identity->blkcg_css;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -458,9 +457,9 @@ static inline void io_wq_switch_blkcg(struct io_worker *worker,
|
||||
static void io_wq_switch_creds(struct io_worker *worker,
|
||||
struct io_wq_work *work)
|
||||
{
|
||||
const struct cred *old_creds = override_creds(work->creds);
|
||||
const struct cred *old_creds = override_creds(work->identity->creds);
|
||||
|
||||
worker->cur_creds = work->creds;
|
||||
worker->cur_creds = work->identity->creds;
|
||||
if (worker->saved_creds)
|
||||
put_cred(old_creds); /* creds set by previous switch */
|
||||
else
|
||||
@@ -470,20 +469,26 @@ static void io_wq_switch_creds(struct io_worker *worker,
|
||||
static void io_impersonate_work(struct io_worker *worker,
|
||||
struct io_wq_work *work)
|
||||
{
|
||||
if (work->files && current->files != work->files) {
|
||||
if ((work->flags & IO_WQ_WORK_FILES) &&
|
||||
current->files != work->identity->files) {
|
||||
task_lock(current);
|
||||
current->files = work->files;
|
||||
current->nsproxy = work->nsproxy;
|
||||
current->files = work->identity->files;
|
||||
current->nsproxy = work->identity->nsproxy;
|
||||
task_unlock(current);
|
||||
}
|
||||
if (work->fs && current->fs != work->fs)
|
||||
current->fs = work->fs;
|
||||
if (work->mm != worker->mm)
|
||||
if ((work->flags & IO_WQ_WORK_FS) && current->fs != work->identity->fs)
|
||||
current->fs = work->identity->fs;
|
||||
if ((work->flags & IO_WQ_WORK_MM) && work->identity->mm != worker->mm)
|
||||
io_wq_switch_mm(worker, work);
|
||||
if (worker->cur_creds != work->creds)
|
||||
if ((work->flags & IO_WQ_WORK_CREDS) &&
|
||||
worker->cur_creds != work->identity->creds)
|
||||
io_wq_switch_creds(worker, work);
|
||||
current->signal->rlim[RLIMIT_FSIZE].rlim_cur = work->fsize;
|
||||
current->signal->rlim[RLIMIT_FSIZE].rlim_cur = work->identity->fsize;
|
||||
io_wq_switch_blkcg(worker, work);
|
||||
#ifdef CONFIG_AUDIT
|
||||
current->loginuid = work->identity->loginuid;
|
||||
current->sessionid = work->identity->sessionid;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void io_assign_current_work(struct io_worker *worker,
|
||||
@@ -496,6 +501,11 @@ static void io_assign_current_work(struct io_worker *worker,
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_AUDIT
|
||||
current->loginuid = KUIDT_INIT(AUDIT_UID_UNSET);
|
||||
current->sessionid = AUDIT_SID_UNSET;
|
||||
#endif
|
||||
|
||||
spin_lock_irq(&worker->lock);
|
||||
worker->cur_work = work;
|
||||
spin_unlock_irq(&worker->lock);
|
||||
@@ -676,6 +686,7 @@ static bool create_io_worker(struct io_wq *wq, struct io_wqe *wqe, int index)
|
||||
kfree(worker);
|
||||
return false;
|
||||
}
|
||||
kthread_bind_mask(worker->task, cpumask_of_node(wqe->node));
|
||||
|
||||
raw_spin_lock_irq(&wqe->lock);
|
||||
hlist_nulls_add_head_rcu(&worker->nulls_node, &wqe->free_list);
|
||||
|
18
fs/io-wq.h
18
fs/io-wq.h
@@ -1,6 +1,8 @@
|
||||
#ifndef INTERNAL_IO_WQ_H
|
||||
#define INTERNAL_IO_WQ_H
|
||||
|
||||
#include <linux/io_uring.h>
|
||||
|
||||
struct io_wq;
|
||||
|
||||
enum {
|
||||
@@ -10,6 +12,12 @@ enum {
|
||||
IO_WQ_WORK_NO_CANCEL = 8,
|
||||
IO_WQ_WORK_CONCURRENT = 16,
|
||||
|
||||
IO_WQ_WORK_FILES = 32,
|
||||
IO_WQ_WORK_FS = 64,
|
||||
IO_WQ_WORK_MM = 128,
|
||||
IO_WQ_WORK_CREDS = 256,
|
||||
IO_WQ_WORK_BLKCG = 512,
|
||||
|
||||
IO_WQ_HASH_SHIFT = 24, /* upper 8 bits are used for hash key */
|
||||
};
|
||||
|
||||
@@ -85,15 +93,7 @@ static inline void wq_list_del(struct io_wq_work_list *list,
|
||||
|
||||
struct io_wq_work {
|
||||
struct io_wq_work_node list;
|
||||
struct files_struct *files;
|
||||
struct mm_struct *mm;
|
||||
#ifdef CONFIG_BLK_CGROUP
|
||||
struct cgroup_subsys_state *blkcg_css;
|
||||
#endif
|
||||
const struct cred *creds;
|
||||
struct nsproxy *nsproxy;
|
||||
struct fs_struct *fs;
|
||||
unsigned long fsize;
|
||||
struct io_identity *identity;
|
||||
unsigned flags;
|
||||
};
|
||||
|
||||
|
670
fs/io_uring.c
670
fs/io_uring.c
File diff suppressed because it is too large
Load Diff
@@ -417,7 +417,7 @@ void nsm_release(struct nsm_handle *nsm)
|
||||
/*
|
||||
* XDR functions for NSM.
|
||||
*
|
||||
* See http://www.opengroup.org/ for details on the Network
|
||||
* See https://www.opengroup.org/ for details on the Network
|
||||
* Status Monitor wire protocol.
|
||||
*/
|
||||
|
||||
|
@@ -94,6 +94,7 @@ enum {
|
||||
static const struct constant_table nfs_param_enums_local_lock[] = {
|
||||
{ "all", Opt_local_lock_all },
|
||||
{ "flock", Opt_local_lock_flock },
|
||||
{ "posix", Opt_local_lock_posix },
|
||||
{ "none", Opt_local_lock_none },
|
||||
{}
|
||||
};
|
||||
|
@@ -32,9 +32,9 @@ int nfs_mountpoint_expiry_timeout = 500 * HZ;
|
||||
/*
|
||||
* nfs_path - reconstruct the path given an arbitrary dentry
|
||||
* @base - used to return pointer to the end of devname part of path
|
||||
* @dentry - pointer to dentry
|
||||
* @dentry_in - pointer to dentry
|
||||
* @buffer - result buffer
|
||||
* @buflen - length of buffer
|
||||
* @buflen_in - length of buffer
|
||||
* @flags - options (see below)
|
||||
*
|
||||
* Helper function for constructing the server pathname
|
||||
@@ -49,15 +49,19 @@ int nfs_mountpoint_expiry_timeout = 500 * HZ;
|
||||
* the original device (export) name
|
||||
* (if unset, the original name is returned verbatim)
|
||||
*/
|
||||
char *nfs_path(char **p, struct dentry *dentry, char *buffer, ssize_t buflen,
|
||||
unsigned flags)
|
||||
char *nfs_path(char **p, struct dentry *dentry_in, char *buffer,
|
||||
ssize_t buflen_in, unsigned flags)
|
||||
{
|
||||
char *end;
|
||||
int namelen;
|
||||
unsigned seq;
|
||||
const char *base;
|
||||
struct dentry *dentry;
|
||||
ssize_t buflen;
|
||||
|
||||
rename_retry:
|
||||
buflen = buflen_in;
|
||||
dentry = dentry_in;
|
||||
end = buffer+buflen;
|
||||
*--end = '\0';
|
||||
buflen--;
|
||||
|
@@ -67,7 +67,6 @@ struct nfs4_xattr_bucket {
|
||||
|
||||
struct nfs4_xattr_cache {
|
||||
struct kref ref;
|
||||
spinlock_t hash_lock; /* protects hashtable and lru */
|
||||
struct nfs4_xattr_bucket buckets[NFS4_XATTR_HASH_SIZE];
|
||||
struct list_head lru;
|
||||
struct list_head dispose;
|
||||
@@ -882,7 +881,7 @@ nfs4_xattr_cache_count(struct shrinker *shrink, struct shrink_control *sc)
|
||||
{
|
||||
unsigned long count;
|
||||
|
||||
count = list_lru_count(&nfs4_xattr_cache_lru);
|
||||
count = list_lru_shrink_count(&nfs4_xattr_cache_lru, sc);
|
||||
return vfs_pressure_ratio(count);
|
||||
}
|
||||
|
||||
@@ -976,7 +975,7 @@ nfs4_xattr_entry_count(struct shrinker *shrink, struct shrink_control *sc)
|
||||
lru = (shrink == &nfs4_xattr_large_entry_shrinker) ?
|
||||
&nfs4_xattr_large_entry_lru : &nfs4_xattr_entry_lru;
|
||||
|
||||
count = list_lru_count(lru);
|
||||
count = list_lru_shrink_count(lru, sc);
|
||||
return vfs_pressure_ratio(count);
|
||||
}
|
||||
|
||||
|
@@ -45,6 +45,15 @@
|
||||
#define encode_deallocate_maxsz (op_encode_hdr_maxsz + \
|
||||
encode_fallocate_maxsz)
|
||||
#define decode_deallocate_maxsz (op_decode_hdr_maxsz)
|
||||
#define encode_read_plus_maxsz (op_encode_hdr_maxsz + \
|
||||
encode_stateid_maxsz + 3)
|
||||
#define NFS42_READ_PLUS_SEGMENT_SIZE (1 /* data_content4 */ + \
|
||||
2 /* data_info4.di_offset */ + \
|
||||
2 /* data_info4.di_length */)
|
||||
#define decode_read_plus_maxsz (op_decode_hdr_maxsz + \
|
||||
1 /* rpr_eof */ + \
|
||||
1 /* rpr_contents count */ + \
|
||||
2 * NFS42_READ_PLUS_SEGMENT_SIZE)
|
||||
#define encode_seek_maxsz (op_encode_hdr_maxsz + \
|
||||
encode_stateid_maxsz + \
|
||||
2 /* offset */ + \
|
||||
@@ -128,6 +137,14 @@
|
||||
decode_putfh_maxsz + \
|
||||
decode_deallocate_maxsz + \
|
||||
decode_getattr_maxsz)
|
||||
#define NFS4_enc_read_plus_sz (compound_encode_hdr_maxsz + \
|
||||
encode_sequence_maxsz + \
|
||||
encode_putfh_maxsz + \
|
||||
encode_read_plus_maxsz)
|
||||
#define NFS4_dec_read_plus_sz (compound_decode_hdr_maxsz + \
|
||||
decode_sequence_maxsz + \
|
||||
decode_putfh_maxsz + \
|
||||
decode_read_plus_maxsz)
|
||||
#define NFS4_enc_seek_sz (compound_encode_hdr_maxsz + \
|
||||
encode_sequence_maxsz + \
|
||||
encode_putfh_maxsz + \
|
||||
@@ -324,6 +341,16 @@ static void encode_deallocate(struct xdr_stream *xdr,
|
||||
encode_fallocate(xdr, args);
|
||||
}
|
||||
|
||||
static void encode_read_plus(struct xdr_stream *xdr,
|
||||
const struct nfs_pgio_args *args,
|
||||
struct compound_hdr *hdr)
|
||||
{
|
||||
encode_op_hdr(xdr, OP_READ_PLUS, decode_read_plus_maxsz, hdr);
|
||||
encode_nfs4_stateid(xdr, &args->stateid);
|
||||
encode_uint64(xdr, args->offset);
|
||||
encode_uint32(xdr, args->count);
|
||||
}
|
||||
|
||||
static void encode_seek(struct xdr_stream *xdr,
|
||||
const struct nfs42_seek_args *args,
|
||||
struct compound_hdr *hdr)
|
||||
@@ -722,6 +749,28 @@ static void nfs4_xdr_enc_deallocate(struct rpc_rqst *req,
|
||||
encode_nops(&hdr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode READ_PLUS request
|
||||
*/
|
||||
static void nfs4_xdr_enc_read_plus(struct rpc_rqst *req,
|
||||
struct xdr_stream *xdr,
|
||||
const void *data)
|
||||
{
|
||||
const struct nfs_pgio_args *args = data;
|
||||
struct compound_hdr hdr = {
|
||||
.minorversion = nfs4_xdr_minorversion(&args->seq_args),
|
||||
};
|
||||
|
||||
encode_compound_hdr(xdr, req, &hdr);
|
||||
encode_sequence(xdr, &args->seq_args, &hdr);
|
||||
encode_putfh(xdr, args->fh, &hdr);
|
||||
encode_read_plus(xdr, args, &hdr);
|
||||
|
||||
rpc_prepare_reply_pages(req, args->pages, args->pgbase,
|
||||
args->count, hdr.replen);
|
||||
encode_nops(&hdr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode SEEK request
|
||||
*/
|
||||
@@ -970,6 +1019,97 @@ static int decode_deallocate(struct xdr_stream *xdr, struct nfs42_falloc_res *re
|
||||
return decode_op_hdr(xdr, OP_DEALLOCATE);
|
||||
}
|
||||
|
||||
static int decode_read_plus_data(struct xdr_stream *xdr, struct nfs_pgio_res *res,
|
||||
uint32_t *eof)
|
||||
{
|
||||
uint32_t count, recvd;
|
||||
uint64_t offset;
|
||||
__be32 *p;
|
||||
|
||||
p = xdr_inline_decode(xdr, 8 + 4);
|
||||
if (unlikely(!p))
|
||||
return -EIO;
|
||||
|
||||
p = xdr_decode_hyper(p, &offset);
|
||||
count = be32_to_cpup(p);
|
||||
recvd = xdr_align_data(xdr, res->count, count);
|
||||
res->count += recvd;
|
||||
|
||||
if (count > recvd) {
|
||||
dprintk("NFS: server cheating in read reply: "
|
||||
"count %u > recvd %u\n", count, recvd);
|
||||
*eof = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int decode_read_plus_hole(struct xdr_stream *xdr, struct nfs_pgio_res *res,
|
||||
uint32_t *eof)
|
||||
{
|
||||
uint64_t offset, length, recvd;
|
||||
__be32 *p;
|
||||
|
||||
p = xdr_inline_decode(xdr, 8 + 8);
|
||||
if (unlikely(!p))
|
||||
return -EIO;
|
||||
|
||||
p = xdr_decode_hyper(p, &offset);
|
||||
p = xdr_decode_hyper(p, &length);
|
||||
recvd = xdr_expand_hole(xdr, res->count, length);
|
||||
res->count += recvd;
|
||||
|
||||
if (recvd < length) {
|
||||
*eof = 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int decode_read_plus(struct xdr_stream *xdr, struct nfs_pgio_res *res)
|
||||
{
|
||||
uint32_t eof, segments, type;
|
||||
int status, i;
|
||||
__be32 *p;
|
||||
|
||||
status = decode_op_hdr(xdr, OP_READ_PLUS);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
p = xdr_inline_decode(xdr, 4 + 4);
|
||||
if (unlikely(!p))
|
||||
return -EIO;
|
||||
|
||||
eof = be32_to_cpup(p++);
|
||||
segments = be32_to_cpup(p++);
|
||||
if (segments == 0)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < segments; i++) {
|
||||
p = xdr_inline_decode(xdr, 4);
|
||||
if (unlikely(!p))
|
||||
return -EIO;
|
||||
|
||||
type = be32_to_cpup(p++);
|
||||
if (type == NFS4_CONTENT_DATA)
|
||||
status = decode_read_plus_data(xdr, res, &eof);
|
||||
else if (type == NFS4_CONTENT_HOLE)
|
||||
status = decode_read_plus_hole(xdr, res, &eof);
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
if (status < 0)
|
||||
return status;
|
||||
if (status > 0)
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
res->eof = eof;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int decode_seek(struct xdr_stream *xdr, struct nfs42_seek_res *res)
|
||||
{
|
||||
int status;
|
||||
@@ -1146,6 +1286,33 @@ out:
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decode READ_PLUS request
|
||||
*/
|
||||
static int nfs4_xdr_dec_read_plus(struct rpc_rqst *rqstp,
|
||||
struct xdr_stream *xdr,
|
||||
void *data)
|
||||
{
|
||||
struct nfs_pgio_res *res = data;
|
||||
struct compound_hdr hdr;
|
||||
int status;
|
||||
|
||||
status = decode_compound_hdr(xdr, &hdr);
|
||||
if (status)
|
||||
goto out;
|
||||
status = decode_sequence(xdr, &res->seq_res, rqstp);
|
||||
if (status)
|
||||
goto out;
|
||||
status = decode_putfh(xdr);
|
||||
if (status)
|
||||
goto out;
|
||||
status = decode_read_plus(xdr, res);
|
||||
if (!status)
|
||||
status = res->count;
|
||||
out:
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decode SEEK request
|
||||
*/
|
||||
|
@@ -599,6 +599,14 @@ static inline bool nfs4_stateid_is_newer(const nfs4_stateid *s1, const nfs4_stat
|
||||
return (s32)(be32_to_cpu(s1->seqid) - be32_to_cpu(s2->seqid)) > 0;
|
||||
}
|
||||
|
||||
static inline bool nfs4_stateid_is_next(const nfs4_stateid *s1, const nfs4_stateid *s2)
|
||||
{
|
||||
u32 seq1 = be32_to_cpu(s1->seqid);
|
||||
u32 seq2 = be32_to_cpu(s2->seqid);
|
||||
|
||||
return seq2 == seq1 + 1U || (seq2 == 1U && seq1 == 0xffffffffU);
|
||||
}
|
||||
|
||||
static inline bool nfs4_stateid_match_or_older(const nfs4_stateid *dst, const nfs4_stateid *src)
|
||||
{
|
||||
return nfs4_stateid_match_other(dst, src) &&
|
||||
|
@@ -1045,6 +1045,8 @@ static int nfs4_server_common_setup(struct nfs_server *server,
|
||||
server->caps |= server->nfs_client->cl_mvops->init_caps;
|
||||
if (server->flags & NFS_MOUNT_NORDIRPLUS)
|
||||
server->caps &= ~NFS_CAP_READDIRPLUS;
|
||||
if (server->nfs_client->cl_proto == XPRT_TRANSPORT_RDMA)
|
||||
server->caps &= ~NFS_CAP_READ_PLUS;
|
||||
/*
|
||||
* Don't use NFS uid/gid mapping if we're using AUTH_SYS or lower
|
||||
* authentication.
|
||||
|
@@ -145,7 +145,8 @@ static ssize_t __nfs4_copy_file_range(struct file *file_in, loff_t pos_in,
|
||||
/* Only offload copy if superblock is the same */
|
||||
if (file_in->f_op != &nfs4_file_operations)
|
||||
return -EXDEV;
|
||||
if (!nfs_server_capable(file_inode(file_out), NFS_CAP_COPY))
|
||||
if (!nfs_server_capable(file_inode(file_out), NFS_CAP_COPY) ||
|
||||
!nfs_server_capable(file_inode(file_in), NFS_CAP_COPY))
|
||||
return -EOPNOTSUPP;
|
||||
if (file_inode(file_in) == file_inode(file_out))
|
||||
return -EOPNOTSUPP;
|
||||
|
@@ -46,6 +46,7 @@
|
||||
#include <keys/user-type.h>
|
||||
#include <keys/request_key_auth-type.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/user_namespace.h>
|
||||
|
||||
#include "internal.h"
|
||||
#include "netns.h"
|
||||
@@ -69,13 +70,13 @@ struct idmap {
|
||||
struct rpc_pipe *idmap_pipe;
|
||||
struct idmap_legacy_upcalldata *idmap_upcall_data;
|
||||
struct mutex idmap_mutex;
|
||||
const struct cred *cred;
|
||||
struct user_namespace *user_ns;
|
||||
};
|
||||
|
||||
static struct user_namespace *idmap_userns(const struct idmap *idmap)
|
||||
{
|
||||
if (idmap && idmap->cred)
|
||||
return idmap->cred->user_ns;
|
||||
if (idmap && idmap->user_ns)
|
||||
return idmap->user_ns;
|
||||
return &init_user_ns;
|
||||
}
|
||||
|
||||
@@ -286,7 +287,7 @@ static struct key *nfs_idmap_request_key(const char *name, size_t namelen,
|
||||
if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
if (!idmap->cred || idmap->cred->user_ns == &init_user_ns)
|
||||
if (!idmap->user_ns || idmap->user_ns == &init_user_ns)
|
||||
rkey = request_key(&key_type_id_resolver, desc, "");
|
||||
if (IS_ERR(rkey)) {
|
||||
mutex_lock(&idmap->idmap_mutex);
|
||||
@@ -462,7 +463,7 @@ nfs_idmap_new(struct nfs_client *clp)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&idmap->idmap_mutex);
|
||||
idmap->cred = get_cred(clp->cl_rpcclient->cl_cred);
|
||||
idmap->user_ns = get_user_ns(clp->cl_rpcclient->cl_cred->user_ns);
|
||||
|
||||
rpc_init_pipe_dir_object(&idmap->idmap_pdo,
|
||||
&nfs_idmap_pipe_dir_object_ops,
|
||||
@@ -486,7 +487,7 @@ nfs_idmap_new(struct nfs_client *clp)
|
||||
err_destroy_pipe:
|
||||
rpc_destroy_pipe_data(idmap->idmap_pipe);
|
||||
err:
|
||||
put_cred(idmap->cred);
|
||||
get_user_ns(idmap->user_ns);
|
||||
kfree(idmap);
|
||||
return error;
|
||||
}
|
||||
@@ -503,7 +504,7 @@ nfs_idmap_delete(struct nfs_client *clp)
|
||||
&clp->cl_rpcclient->cl_pipedir_objects,
|
||||
&idmap->idmap_pdo);
|
||||
rpc_destroy_pipe_data(idmap->idmap_pipe);
|
||||
put_cred(idmap->cred);
|
||||
put_user_ns(idmap->user_ns);
|
||||
kfree(idmap);
|
||||
}
|
||||
|
||||
|
@@ -63,6 +63,7 @@
|
||||
#include "callback.h"
|
||||
#include "pnfs.h"
|
||||
#include "netns.h"
|
||||
#include "sysfs.h"
|
||||
#include "nfs4idmap.h"
|
||||
#include "nfs4session.h"
|
||||
#include "fscache.h"
|
||||
@@ -70,6 +71,10 @@
|
||||
|
||||
#include "nfs4trace.h"
|
||||
|
||||
#ifdef CONFIG_NFS_V4_2
|
||||
#include "nfs42.h"
|
||||
#endif /* CONFIG_NFS_V4_2 */
|
||||
|
||||
#define NFSDBG_FACILITY NFSDBG_PROC
|
||||
|
||||
#define NFS4_BITMASK_SZ 3
|
||||
@@ -107,6 +112,9 @@ static int nfs41_test_stateid(struct nfs_server *, nfs4_stateid *,
|
||||
static int nfs41_free_stateid(struct nfs_server *, const nfs4_stateid *,
|
||||
const struct cred *, bool);
|
||||
#endif
|
||||
static void nfs4_bitmask_adjust(__u32 *bitmask, struct inode *inode,
|
||||
struct nfs_server *server,
|
||||
struct nfs4_label *label);
|
||||
|
||||
#ifdef CONFIG_NFS_V4_SECURITY_LABEL
|
||||
static inline struct nfs4_label *
|
||||
@@ -1547,19 +1555,6 @@ static void nfs_state_log_update_open_stateid(struct nfs4_state *state)
|
||||
wake_up_all(&state->waitq);
|
||||
}
|
||||
|
||||
static void nfs_state_log_out_of_order_open_stateid(struct nfs4_state *state,
|
||||
const nfs4_stateid *stateid)
|
||||
{
|
||||
u32 state_seqid = be32_to_cpu(state->open_stateid.seqid);
|
||||
u32 stateid_seqid = be32_to_cpu(stateid->seqid);
|
||||
|
||||
if (stateid_seqid == state_seqid + 1U ||
|
||||
(stateid_seqid == 1U && state_seqid == 0xffffffffU))
|
||||
nfs_state_log_update_open_stateid(state);
|
||||
else
|
||||
set_bit(NFS_STATE_CHANGE_WAIT, &state->flags);
|
||||
}
|
||||
|
||||
static void nfs_test_and_clear_all_open_stateid(struct nfs4_state *state)
|
||||
{
|
||||
struct nfs_client *clp = state->owner->so_server->nfs_client;
|
||||
@@ -1585,21 +1580,19 @@ static void nfs_test_and_clear_all_open_stateid(struct nfs4_state *state)
|
||||
* i.e. The stateid seqids have to be initialised to 1, and
|
||||
* are then incremented on every state transition.
|
||||
*/
|
||||
static bool nfs_need_update_open_stateid(struct nfs4_state *state,
|
||||
static bool nfs_stateid_is_sequential(struct nfs4_state *state,
|
||||
const nfs4_stateid *stateid)
|
||||
{
|
||||
if (test_bit(NFS_OPEN_STATE, &state->flags) == 0 ||
|
||||
!nfs4_stateid_match_other(stateid, &state->open_stateid)) {
|
||||
if (test_bit(NFS_OPEN_STATE, &state->flags)) {
|
||||
/* The common case - we're updating to a new sequence number */
|
||||
if (nfs4_stateid_match_other(stateid, &state->open_stateid) &&
|
||||
nfs4_stateid_is_next(&state->open_stateid, stateid)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
/* This is the first OPEN in this generation */
|
||||
if (stateid->seqid == cpu_to_be32(1))
|
||||
nfs_state_log_update_open_stateid(state);
|
||||
else
|
||||
set_bit(NFS_STATE_CHANGE_WAIT, &state->flags);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (nfs4_stateid_is_newer(stateid, &state->open_stateid)) {
|
||||
nfs_state_log_out_of_order_open_stateid(state, stateid);
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1673,16 +1666,16 @@ static void nfs_set_open_stateid_locked(struct nfs4_state *state,
|
||||
int status = 0;
|
||||
for (;;) {
|
||||
|
||||
if (!nfs_need_update_open_stateid(state, stateid))
|
||||
return;
|
||||
if (!test_bit(NFS_STATE_CHANGE_WAIT, &state->flags))
|
||||
if (nfs_stateid_is_sequential(state, stateid))
|
||||
break;
|
||||
|
||||
if (status)
|
||||
break;
|
||||
/* Rely on seqids for serialisation with NFSv4.0 */
|
||||
if (!nfs4_has_session(NFS_SERVER(state->inode)->nfs_client))
|
||||
break;
|
||||
|
||||
set_bit(NFS_STATE_CHANGE_WAIT, &state->flags);
|
||||
prepare_to_wait(&state->waitq, &wait, TASK_KILLABLE);
|
||||
/*
|
||||
* Ensure we process the state changes in the same order
|
||||
@@ -1693,6 +1686,7 @@ static void nfs_set_open_stateid_locked(struct nfs4_state *state,
|
||||
spin_unlock(&state->owner->so_lock);
|
||||
rcu_read_unlock();
|
||||
trace_nfs4_open_stateid_update_wait(state->inode, stateid, 0);
|
||||
|
||||
if (!signal_pending(current)) {
|
||||
if (schedule_timeout(5*HZ) == 0)
|
||||
status = -EAGAIN;
|
||||
@@ -3435,7 +3429,8 @@ static bool nfs4_refresh_open_old_stateid(nfs4_stateid *dst,
|
||||
__be32 seqid_open;
|
||||
u32 dst_seqid;
|
||||
bool ret;
|
||||
int seq;
|
||||
int seq, status = -EAGAIN;
|
||||
DEFINE_WAIT(wait);
|
||||
|
||||
for (;;) {
|
||||
ret = false;
|
||||
@@ -3447,15 +3442,41 @@ static bool nfs4_refresh_open_old_stateid(nfs4_stateid *dst,
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
write_seqlock(&state->seqlock);
|
||||
seqid_open = state->open_stateid.seqid;
|
||||
if (read_seqretry(&state->seqlock, seq))
|
||||
continue;
|
||||
|
||||
dst_seqid = be32_to_cpu(dst->seqid);
|
||||
if ((s32)(dst_seqid - be32_to_cpu(seqid_open)) >= 0)
|
||||
dst->seqid = cpu_to_be32(dst_seqid + 1);
|
||||
else
|
||||
|
||||
/* Did another OPEN bump the state's seqid? try again: */
|
||||
if ((s32)(be32_to_cpu(seqid_open) - dst_seqid) > 0) {
|
||||
dst->seqid = seqid_open;
|
||||
write_sequnlock(&state->seqlock);
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/* server says we're behind but we haven't seen the update yet */
|
||||
set_bit(NFS_STATE_CHANGE_WAIT, &state->flags);
|
||||
prepare_to_wait(&state->waitq, &wait, TASK_KILLABLE);
|
||||
write_sequnlock(&state->seqlock);
|
||||
trace_nfs4_close_stateid_update_wait(state->inode, dst, 0);
|
||||
|
||||
if (signal_pending(current))
|
||||
status = -EINTR;
|
||||
else
|
||||
if (schedule_timeout(5*HZ) != 0)
|
||||
status = 0;
|
||||
|
||||
finish_wait(&state->waitq, &wait);
|
||||
|
||||
if (!status)
|
||||
continue;
|
||||
if (status == -EINTR)
|
||||
break;
|
||||
|
||||
/* we slept the whole 5 seconds, we must have lost a seqid */
|
||||
dst->seqid = cpu_to_be32(dst_seqid + 1);
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
@@ -3632,9 +3653,10 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
|
||||
|
||||
if (calldata->arg.fmode == 0 || calldata->arg.fmode == FMODE_READ) {
|
||||
/* Close-to-open cache consistency revalidation */
|
||||
if (!nfs4_have_delegation(inode, FMODE_READ))
|
||||
if (!nfs4_have_delegation(inode, FMODE_READ)) {
|
||||
calldata->arg.bitmask = NFS_SERVER(inode)->cache_consistency_bitmask;
|
||||
else
|
||||
nfs4_bitmask_adjust(calldata->arg.bitmask, inode, NFS_SERVER(inode), NULL);
|
||||
} else
|
||||
calldata->arg.bitmask = NULL;
|
||||
}
|
||||
|
||||
@@ -5255,28 +5277,60 @@ static bool nfs4_read_stateid_changed(struct rpc_task *task,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool nfs4_read_plus_not_supported(struct rpc_task *task,
|
||||
struct nfs_pgio_header *hdr)
|
||||
{
|
||||
struct nfs_server *server = NFS_SERVER(hdr->inode);
|
||||
struct rpc_message *msg = &task->tk_msg;
|
||||
|
||||
if (msg->rpc_proc == &nfs4_procedures[NFSPROC4_CLNT_READ_PLUS] &&
|
||||
server->caps & NFS_CAP_READ_PLUS && task->tk_status == -ENOTSUPP) {
|
||||
server->caps &= ~NFS_CAP_READ_PLUS;
|
||||
msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ];
|
||||
rpc_restart_call_prepare(task);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int nfs4_read_done(struct rpc_task *task, struct nfs_pgio_header *hdr)
|
||||
{
|
||||
|
||||
dprintk("--> %s\n", __func__);
|
||||
|
||||
if (!nfs4_sequence_done(task, &hdr->res.seq_res))
|
||||
return -EAGAIN;
|
||||
if (nfs4_read_stateid_changed(task, &hdr->args))
|
||||
return -EAGAIN;
|
||||
if (nfs4_read_plus_not_supported(task, hdr))
|
||||
return -EAGAIN;
|
||||
if (task->tk_status > 0)
|
||||
nfs_invalidate_atime(hdr->inode);
|
||||
return hdr->pgio_done_cb ? hdr->pgio_done_cb(task, hdr) :
|
||||
nfs4_read_done_cb(task, hdr);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NFS_V4_2
|
||||
static void nfs42_read_plus_support(struct nfs_server *server, struct rpc_message *msg)
|
||||
{
|
||||
if (server->caps & NFS_CAP_READ_PLUS)
|
||||
msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ_PLUS];
|
||||
else
|
||||
msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ];
|
||||
}
|
||||
#else
|
||||
static void nfs42_read_plus_support(struct nfs_server *server, struct rpc_message *msg)
|
||||
{
|
||||
msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ];
|
||||
}
|
||||
#endif /* CONFIG_NFS_V4_2 */
|
||||
|
||||
static void nfs4_proc_read_setup(struct nfs_pgio_header *hdr,
|
||||
struct rpc_message *msg)
|
||||
{
|
||||
hdr->timestamp = jiffies;
|
||||
if (!hdr->pgio_done_cb)
|
||||
hdr->pgio_done_cb = nfs4_read_done_cb;
|
||||
msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READ];
|
||||
nfs42_read_plus_support(NFS_SERVER(hdr->inode), msg);
|
||||
nfs4_init_sequence(&hdr->args.seq_args, &hdr->res.seq_res, 0, 0);
|
||||
}
|
||||
|
||||
@@ -5360,6 +5414,38 @@ bool nfs4_write_need_cache_consistency_data(struct nfs_pgio_header *hdr)
|
||||
return nfs4_have_delegation(hdr->inode, FMODE_READ) == 0;
|
||||
}
|
||||
|
||||
static void nfs4_bitmask_adjust(__u32 *bitmask, struct inode *inode,
|
||||
struct nfs_server *server,
|
||||
struct nfs4_label *label)
|
||||
{
|
||||
|
||||
unsigned long cache_validity = READ_ONCE(NFS_I(inode)->cache_validity);
|
||||
|
||||
if ((cache_validity & NFS_INO_INVALID_DATA) ||
|
||||
(cache_validity & NFS_INO_REVAL_PAGECACHE) ||
|
||||
(cache_validity & NFS_INO_REVAL_FORCED) ||
|
||||
(cache_validity & NFS_INO_INVALID_OTHER))
|
||||
nfs4_bitmap_copy_adjust(bitmask, nfs4_bitmask(server, label), inode);
|
||||
|
||||
if (cache_validity & NFS_INO_INVALID_ATIME)
|
||||
bitmask[1] |= FATTR4_WORD1_TIME_ACCESS;
|
||||
if (cache_validity & NFS_INO_INVALID_ACCESS)
|
||||
bitmask[0] |= FATTR4_WORD1_MODE | FATTR4_WORD1_OWNER |
|
||||
FATTR4_WORD1_OWNER_GROUP;
|
||||
if (cache_validity & NFS_INO_INVALID_ACL)
|
||||
bitmask[0] |= FATTR4_WORD0_ACL;
|
||||
if (cache_validity & NFS_INO_INVALID_LABEL)
|
||||
bitmask[2] |= FATTR4_WORD2_SECURITY_LABEL;
|
||||
if (cache_validity & NFS_INO_INVALID_CTIME)
|
||||
bitmask[0] |= FATTR4_WORD0_CHANGE;
|
||||
if (cache_validity & NFS_INO_INVALID_MTIME)
|
||||
bitmask[1] |= FATTR4_WORD1_TIME_MODIFY;
|
||||
if (cache_validity & NFS_INO_INVALID_SIZE)
|
||||
bitmask[0] |= FATTR4_WORD0_SIZE;
|
||||
if (cache_validity & NFS_INO_INVALID_BLOCKS)
|
||||
bitmask[1] |= FATTR4_WORD1_SPACE_USED;
|
||||
}
|
||||
|
||||
static void nfs4_proc_write_setup(struct nfs_pgio_header *hdr,
|
||||
struct rpc_message *msg,
|
||||
struct rpc_clnt **clnt)
|
||||
@@ -5369,8 +5455,10 @@ static void nfs4_proc_write_setup(struct nfs_pgio_header *hdr,
|
||||
if (!nfs4_write_need_cache_consistency_data(hdr)) {
|
||||
hdr->args.bitmask = NULL;
|
||||
hdr->res.fattr = NULL;
|
||||
} else
|
||||
} else {
|
||||
hdr->args.bitmask = server->cache_consistency_bitmask;
|
||||
nfs4_bitmask_adjust(hdr->args.bitmask, hdr->inode, server, NULL);
|
||||
}
|
||||
|
||||
if (!hdr->pgio_done_cb)
|
||||
hdr->pgio_done_cb = nfs4_write_done_cb;
|
||||
@@ -6006,9 +6094,34 @@ static void nfs4_init_boot_verifier(const struct nfs_client *clp,
|
||||
memcpy(bootverf->data, verf, sizeof(bootverf->data));
|
||||
}
|
||||
|
||||
static size_t
|
||||
nfs4_get_uniquifier(struct nfs_client *clp, char *buf, size_t buflen)
|
||||
{
|
||||
struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
|
||||
struct nfs_netns_client *nn_clp = nn->nfs_client;
|
||||
const char *id;
|
||||
|
||||
buf[0] = '\0';
|
||||
|
||||
if (nn_clp) {
|
||||
rcu_read_lock();
|
||||
id = rcu_dereference(nn_clp->identifier);
|
||||
if (id)
|
||||
strscpy(buf, id, buflen);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
if (nfs4_client_id_uniquifier[0] != '\0' && buf[0] == '\0')
|
||||
strscpy(buf, nfs4_client_id_uniquifier, buflen);
|
||||
|
||||
return strlen(buf);
|
||||
}
|
||||
|
||||
static int
|
||||
nfs4_init_nonuniform_client_string(struct nfs_client *clp)
|
||||
{
|
||||
char buf[NFS4_CLIENT_ID_UNIQ_LEN];
|
||||
size_t buflen;
|
||||
size_t len;
|
||||
char *str;
|
||||
|
||||
@@ -6022,8 +6135,11 @@ nfs4_init_nonuniform_client_string(struct nfs_client *clp)
|
||||
strlen(rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR)) +
|
||||
1;
|
||||
rcu_read_unlock();
|
||||
if (nfs4_client_id_uniquifier[0] != '\0')
|
||||
len += strlen(nfs4_client_id_uniquifier) + 1;
|
||||
|
||||
buflen = nfs4_get_uniquifier(clp, buf, sizeof(buf));
|
||||
if (buflen)
|
||||
len += buflen + 1;
|
||||
|
||||
if (len > NFS4_OPAQUE_LIMIT + 1)
|
||||
return -EINVAL;
|
||||
|
||||
@@ -6037,10 +6153,9 @@ nfs4_init_nonuniform_client_string(struct nfs_client *clp)
|
||||
return -ENOMEM;
|
||||
|
||||
rcu_read_lock();
|
||||
if (nfs4_client_id_uniquifier[0] != '\0')
|
||||
if (buflen)
|
||||
scnprintf(str, len, "Linux NFSv4.0 %s/%s/%s",
|
||||
clp->cl_rpcclient->cl_nodename,
|
||||
nfs4_client_id_uniquifier,
|
||||
clp->cl_rpcclient->cl_nodename, buf,
|
||||
rpc_peeraddr2str(clp->cl_rpcclient,
|
||||
RPC_DISPLAY_ADDR));
|
||||
else
|
||||
@@ -6054,51 +6169,24 @@ nfs4_init_nonuniform_client_string(struct nfs_client *clp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nfs4_init_uniquifier_client_string(struct nfs_client *clp)
|
||||
{
|
||||
size_t len;
|
||||
char *str;
|
||||
|
||||
len = 10 + 10 + 1 + 10 + 1 +
|
||||
strlen(nfs4_client_id_uniquifier) + 1 +
|
||||
strlen(clp->cl_rpcclient->cl_nodename) + 1;
|
||||
|
||||
if (len > NFS4_OPAQUE_LIMIT + 1)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Since this string is allocated at mount time, and held until the
|
||||
* nfs_client is destroyed, we can use GFP_KERNEL here w/o worrying
|
||||
* about a memory-reclaim deadlock.
|
||||
*/
|
||||
str = kmalloc(len, GFP_KERNEL);
|
||||
if (!str)
|
||||
return -ENOMEM;
|
||||
|
||||
scnprintf(str, len, "Linux NFSv%u.%u %s/%s",
|
||||
clp->rpc_ops->version, clp->cl_minorversion,
|
||||
nfs4_client_id_uniquifier,
|
||||
clp->cl_rpcclient->cl_nodename);
|
||||
clp->cl_owner_id = str;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nfs4_init_uniform_client_string(struct nfs_client *clp)
|
||||
{
|
||||
char buf[NFS4_CLIENT_ID_UNIQ_LEN];
|
||||
size_t buflen;
|
||||
size_t len;
|
||||
char *str;
|
||||
|
||||
if (clp->cl_owner_id != NULL)
|
||||
return 0;
|
||||
|
||||
if (nfs4_client_id_uniquifier[0] != '\0')
|
||||
return nfs4_init_uniquifier_client_string(clp);
|
||||
|
||||
len = 10 + 10 + 1 + 10 + 1 +
|
||||
strlen(clp->cl_rpcclient->cl_nodename) + 1;
|
||||
|
||||
buflen = nfs4_get_uniquifier(clp, buf, sizeof(buf));
|
||||
if (buflen)
|
||||
len += buflen + 1;
|
||||
|
||||
if (len > NFS4_OPAQUE_LIMIT + 1)
|
||||
return -EINVAL;
|
||||
|
||||
@@ -6111,9 +6199,14 @@ nfs4_init_uniform_client_string(struct nfs_client *clp)
|
||||
if (!str)
|
||||
return -ENOMEM;
|
||||
|
||||
scnprintf(str, len, "Linux NFSv%u.%u %s",
|
||||
clp->rpc_ops->version, clp->cl_minorversion,
|
||||
clp->cl_rpcclient->cl_nodename);
|
||||
if (buflen)
|
||||
scnprintf(str, len, "Linux NFSv%u.%u %s/%s",
|
||||
clp->rpc_ops->version, clp->cl_minorversion,
|
||||
buf, clp->cl_rpcclient->cl_nodename);
|
||||
else
|
||||
scnprintf(str, len, "Linux NFSv%u.%u %s",
|
||||
clp->rpc_ops->version, clp->cl_minorversion,
|
||||
clp->cl_rpcclient->cl_nodename);
|
||||
clp->cl_owner_id = str;
|
||||
return 0;
|
||||
}
|
||||
@@ -6406,6 +6499,7 @@ static int _nfs4_proc_delegreturn(struct inode *inode, const struct cred *cred,
|
||||
data->args.fhandle = &data->fh;
|
||||
data->args.stateid = &data->stateid;
|
||||
data->args.bitmask = server->cache_consistency_bitmask;
|
||||
nfs4_bitmask_adjust(data->args.bitmask, inode, server, NULL);
|
||||
nfs_copy_fh(&data->fh, NFS_FH(inode));
|
||||
nfs4_stateid_copy(&data->stateid, stateid);
|
||||
data->res.fattr = &data->fattr;
|
||||
@@ -7442,7 +7536,7 @@ nfs4_listxattr_nfs4_label(struct inode *inode, char *list, size_t list_len)
|
||||
|
||||
if (nfs_server_capable(inode, NFS_CAP_SECURITY_LABEL)) {
|
||||
len = security_inode_listsecurity(inode, list, list_len);
|
||||
if (list_len && len > list_len)
|
||||
if (len >= 0 && list_len && len > list_len)
|
||||
return -ERANGE;
|
||||
}
|
||||
return len;
|
||||
@@ -8042,9 +8136,11 @@ int nfs4_proc_secinfo(struct inode *dir, const struct qstr *name,
|
||||
* both PNFS and NON_PNFS flags set, and not having one of NON_PNFS, PNFS, or
|
||||
* DS flags set.
|
||||
*/
|
||||
static int nfs4_check_cl_exchange_flags(u32 flags)
|
||||
static int nfs4_check_cl_exchange_flags(u32 flags, u32 version)
|
||||
{
|
||||
if (flags & ~EXCHGID4_FLAG_MASK_R)
|
||||
if (version >= 2 && (flags & ~EXCHGID4_2_FLAG_MASK_R))
|
||||
goto out_inval;
|
||||
else if (version < 2 && (flags & ~EXCHGID4_FLAG_MASK_R))
|
||||
goto out_inval;
|
||||
if ((flags & EXCHGID4_FLAG_USE_PNFS_MDS) &&
|
||||
(flags & EXCHGID4_FLAG_USE_NON_PNFS))
|
||||
@@ -8457,7 +8553,8 @@ static int _nfs4_proc_exchange_id(struct nfs_client *clp, const struct cred *cre
|
||||
if (status != 0)
|
||||
goto out;
|
||||
|
||||
status = nfs4_check_cl_exchange_flags(resp->flags);
|
||||
status = nfs4_check_cl_exchange_flags(resp->flags,
|
||||
clp->cl_mvops->minor_version);
|
||||
if (status != 0)
|
||||
goto out;
|
||||
|
||||
@@ -9696,7 +9793,6 @@ _nfs41_proc_secinfo_no_name(struct nfs_server *server, struct nfs_fh *fhandle,
|
||||
.rpc_argp = &args,
|
||||
.rpc_resp = &res,
|
||||
};
|
||||
struct rpc_clnt *clnt = server->client;
|
||||
struct nfs4_call_sync_data data = {
|
||||
.seq_server = server,
|
||||
.seq_args = &args.seq_args,
|
||||
@@ -9713,8 +9809,7 @@ _nfs41_proc_secinfo_no_name(struct nfs_server *server, struct nfs_fh *fhandle,
|
||||
int status;
|
||||
|
||||
if (use_integrity) {
|
||||
clnt = server->nfs_client->cl_rpcclient;
|
||||
task_setup.rpc_client = clnt;
|
||||
task_setup.rpc_client = server->nfs_client->cl_rpcclient;
|
||||
|
||||
cred = nfs4_get_clid_cred(server->nfs_client);
|
||||
msg.rpc_cred = cred;
|
||||
@@ -10168,7 +10263,8 @@ static const struct nfs4_minor_version_ops nfs_v4_2_minor_ops = {
|
||||
| NFS_CAP_SEEK
|
||||
| NFS_CAP_LAYOUTSTATS
|
||||
| NFS_CAP_CLONE
|
||||
| NFS_CAP_LAYOUTERROR,
|
||||
| NFS_CAP_LAYOUTERROR
|
||||
| NFS_CAP_READ_PLUS,
|
||||
.init_client = nfs41_init_client,
|
||||
.shutdown_client = nfs41_shutdown_client,
|
||||
.match_stateid = nfs41_match_stateid,
|
||||
|
@@ -1511,6 +1511,7 @@ DEFINE_NFS4_INODE_STATEID_EVENT(nfs4_setattr);
|
||||
DEFINE_NFS4_INODE_STATEID_EVENT(nfs4_delegreturn);
|
||||
DEFINE_NFS4_INODE_STATEID_EVENT(nfs4_open_stateid_update);
|
||||
DEFINE_NFS4_INODE_STATEID_EVENT(nfs4_open_stateid_update_wait);
|
||||
DEFINE_NFS4_INODE_STATEID_EVENT(nfs4_close_stateid_update_wait);
|
||||
|
||||
DECLARE_EVENT_CLASS(nfs4_getattr_event,
|
||||
TP_PROTO(
|
||||
|
@@ -5308,7 +5308,6 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
|
||||
uint32_t attrlen,
|
||||
bitmap[3] = {0};
|
||||
int status;
|
||||
unsigned int pg_offset;
|
||||
|
||||
res->acl_len = 0;
|
||||
if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0)
|
||||
@@ -5316,9 +5315,6 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
|
||||
|
||||
xdr_enter_page(xdr, xdr->buf->page_len);
|
||||
|
||||
/* Calculate the offset of the page data */
|
||||
pg_offset = xdr->buf->head[0].iov_len;
|
||||
|
||||
if ((status = decode_attr_bitmap(xdr, bitmap)) != 0)
|
||||
goto out;
|
||||
if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0)
|
||||
@@ -5331,7 +5327,7 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
|
||||
/* The bitmap (xdr len + bitmaps) and the attr xdr len words
|
||||
* are stored with the acl data to handle the problem of
|
||||
* variable length bitmaps.*/
|
||||
res->acl_data_offset = xdr_stream_pos(xdr) - pg_offset;
|
||||
res->acl_data_offset = xdr_page_pos(xdr);
|
||||
res->acl_len = attrlen;
|
||||
|
||||
/* Check for receive buffer overflow */
|
||||
@@ -7619,6 +7615,7 @@ const struct rpc_procinfo nfs4_procedures[] = {
|
||||
PROC42(SETXATTR, enc_setxattr, dec_setxattr),
|
||||
PROC42(LISTXATTRS, enc_listxattrs, dec_listxattrs),
|
||||
PROC42(REMOVEXATTR, enc_removexattr, dec_removexattr),
|
||||
PROC42(READ_PLUS, enc_read_plus, dec_read_plus),
|
||||
};
|
||||
|
||||
static unsigned int nfs_version4_counts[ARRAY_SIZE(nfs4_procedures)];
|
||||
|
@@ -902,7 +902,7 @@ restart:
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by the state manger to remove all layouts established under an
|
||||
* Called by the state manager to remove all layouts established under an
|
||||
* expired lease.
|
||||
*/
|
||||
void
|
||||
|
@@ -889,7 +889,7 @@ static struct nfs_server *nfs_try_mount_request(struct fs_context *fc)
|
||||
default:
|
||||
if (rpcauth_get_gssinfo(flavor, &info) != 0)
|
||||
continue;
|
||||
/* Fallthrough */
|
||||
break;
|
||||
}
|
||||
dfprintk(MOUNT, "NFS: attempting to use auth flavor %u\n", flavor);
|
||||
ctx->selected_flavor = flavor;
|
||||
|
@@ -79,7 +79,12 @@ static ssize_t nfs_netns_identifier_show(struct kobject *kobj,
|
||||
struct nfs_netns_client *c = container_of(kobj,
|
||||
struct nfs_netns_client,
|
||||
kobject);
|
||||
return scnprintf(buf, PAGE_SIZE, "%s\n", c->identifier);
|
||||
ssize_t ret;
|
||||
|
||||
rcu_read_lock();
|
||||
ret = scnprintf(buf, PAGE_SIZE, "%s\n", rcu_dereference(c->identifier));
|
||||
rcu_read_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Strip trailing '\n' */
|
||||
@@ -107,7 +112,7 @@ static ssize_t nfs_netns_identifier_store(struct kobject *kobj,
|
||||
p = kmemdup_nul(buf, len, GFP_KERNEL);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
old = xchg(&c->identifier, p);
|
||||
old = rcu_dereference_protected(xchg(&c->identifier, (char __rcu *)p), 1);
|
||||
if (old) {
|
||||
synchronize_rcu();
|
||||
kfree(old);
|
||||
@@ -121,7 +126,7 @@ static void nfs_netns_client_release(struct kobject *kobj)
|
||||
struct nfs_netns_client,
|
||||
kobject);
|
||||
|
||||
kfree(c->identifier);
|
||||
kfree(rcu_dereference_raw(c->identifier));
|
||||
kfree(c);
|
||||
}
|
||||
|
||||
|
@@ -11,7 +11,7 @@
|
||||
struct nfs_netns_client {
|
||||
struct kobject kobject;
|
||||
struct net *net;
|
||||
const char *identifier;
|
||||
const char __rcu *identifier;
|
||||
};
|
||||
|
||||
extern struct kobject *nfs_client_kobj;
|
||||
|
@@ -1269,6 +1269,10 @@ static ssize_t proc_loginuid_write(struct file * file, const char __user * buf,
|
||||
kuid_t kloginuid;
|
||||
int rv;
|
||||
|
||||
/* Don't let kthreads write their own loginuid */
|
||||
if (current->flags & PF_KTHREAD)
|
||||
return -EPERM;
|
||||
|
||||
rcu_read_lock();
|
||||
if (current != pid_task(proc_pid(inode), PIDTYPE_PID)) {
|
||||
rcu_read_unlock();
|
||||
|
@@ -54,7 +54,7 @@ struct ceph_connection_operations {
|
||||
int (*check_message_signature) (struct ceph_msg *msg);
|
||||
};
|
||||
|
||||
/* use format string %s%d */
|
||||
/* use format string %s%lld */
|
||||
#define ENTITY_NAME(n) ceph_entity_type_name((n).type), le64_to_cpu((n).num)
|
||||
|
||||
struct ceph_messenger {
|
||||
|
@@ -142,7 +142,7 @@ int ceph_monc_get_version(struct ceph_mon_client *monc, const char *what,
|
||||
int ceph_monc_get_version_async(struct ceph_mon_client *monc, const char *what,
|
||||
ceph_monc_callback_t cb, u64 private_data);
|
||||
|
||||
int ceph_monc_blacklist_add(struct ceph_mon_client *monc,
|
||||
int ceph_monc_blocklist_add(struct ceph_mon_client *monc,
|
||||
struct ceph_entity_addr *client_addr);
|
||||
|
||||
extern int ceph_monc_open_session(struct ceph_mon_client *monc);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user