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:
Greg Kroah-Hartman
2020-10-26 10:23:06 +01:00
143 changed files with 7707 additions and 2595 deletions

View File

@@ -114,6 +114,9 @@ properties:
- const: renesas,r1ex24128
- const: atmel,24c128
label:
description: Descriptive name of the EEPROM.
reg:
maxItems: 1

View File

@@ -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>;

View File

@@ -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:
- |

View File

@@ -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)
--------------------------------------

View File

@@ -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>;

View 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>;
};

View File

@@ -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>;
};
};
...

View File

@@ -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 {

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -47,6 +47,7 @@ Slave I2C
slave-interface
slave-eeprom-backend
slave-testunit-backend
Advanced topics
===============

View 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

View File

@@ -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

View File

@@ -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);

View File

@@ -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[] = "";

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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))

View File

@@ -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);

View File

@@ -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)

View File

@@ -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;

View File

@@ -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);

View File

@@ -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,

View File

@@ -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,
},
};

File diff suppressed because it is too large Load Diff

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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) {

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View 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");

View File

@@ -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.

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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(&current_alarm, 0, sizeof(struct rtc_wkalrm));
cmos_read_alarm(dev, &current_alarm);
t_current_expires = rtc_tm_to_time64(&current_alarm.time);
t_saved_expires = rtc_tm_to_time64(&cmos->saved_wkalrm.time);

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -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);

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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 = {

View File

@@ -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");

View File

@@ -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
View 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, &regmap_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");

View File

@@ -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,

View File

@@ -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,
};

View File

@@ -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);

View File

@@ -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,

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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++) {

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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 */

View File

@@ -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);

View File

@@ -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 */
};

View File

@@ -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;

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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)
{

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;
};

File diff suppressed because it is too large Load Diff

View File

@@ -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.
*/

View File

@@ -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 },
{}
};

View File

@@ -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--;

View File

@@ -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);
}

View File

@@ -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
*/

View File

@@ -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) &&

View File

@@ -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.

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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,

View File

@@ -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(

View File

@@ -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)];

View File

@@ -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

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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();

View File

@@ -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 {

View File

@@ -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