forked from Minki/linux
Merge branch 'i2c/for-5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang: - new drivers for Silicon Labs CP2615 and the HiSilicon I2C unit - bigger refactoring for the MPC driver - support for full software nodes - no need to work around with only properties anymore - we now have 'devm_i2c_add_adapter', too - sub-system wide fixes for the RPM refcounting problem which often caused a leak when an error was encountered during probe - the rest is usual driver updates and improvements * 'i2c/for-5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (77 commits) i2c: mediatek: Use scl_int_delay_ns to compensate clock-stretching i2c: mediatek: Fix wrong dma sync flag i2c: mediatek: Fix send master code at more than 1MHz i2c: sh7760: fix IRQ error path i2c: i801: Add support for Intel Alder Lake PCH-M i2c: core: Fix spacing error by checkpatch i2c: s3c2410: simplify getting of_device_id match data i2c: nomadik: Fix space errors i2c: iop3xx: Fix coding style issues i2c: amd8111: Fix coding style issues i2c: mpc: Drop duplicate message from devm_platform_ioremap_resource() i2c: mpc: Use device_get_match_data() helper i2c: mpc: Remove CONFIG_PM_SLEEP ifdeffery i2c: mpc: Use devm_clk_get_optional() i2c: mpc: Update license and copyright i2c: mpc: Interrupt driven transfer i2c: sh7760: add IRQ check i2c: rcar: add IRQ check i2c: mlxbf: add IRQ check i2c: jz4780: add IRQ check ...
This commit is contained in:
commit
592fa9532d
@ -1,62 +0,0 @@
|
||||
* I2C
|
||||
|
||||
Required properties :
|
||||
|
||||
- reg : Offset and length of the register set for the device
|
||||
- compatible : should be "fsl,CHIP-i2c" where CHIP is the name of a
|
||||
compatible processor, e.g. mpc8313, mpc8543, mpc8544, mpc5121,
|
||||
mpc5200 or mpc5200b. For the mpc5121, an additional node
|
||||
"fsl,mpc5121-i2c-ctrl" is required as shown in the example below.
|
||||
|
||||
Recommended properties :
|
||||
|
||||
- interrupts : <a b> where a is the interrupt number and b is a
|
||||
field that represents an encoding of the sense and level
|
||||
information for the interrupt. This should be encoded based on
|
||||
the information in section 2) depending on the type of interrupt
|
||||
controller you have.
|
||||
- fsl,preserve-clocking : boolean; if defined, the clock settings
|
||||
from the bootloader are preserved (not touched).
|
||||
- clock-frequency : desired I2C bus clock frequency in Hz.
|
||||
- fsl,timeout : I2C bus timeout in microseconds.
|
||||
|
||||
Examples :
|
||||
|
||||
/* MPC5121 based board */
|
||||
i2c@1740 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "fsl,mpc5121-i2c", "fsl-i2c";
|
||||
reg = <0x1740 0x20>;
|
||||
interrupts = <11 0x8>;
|
||||
interrupt-parent = <&ipic>;
|
||||
clock-frequency = <100000>;
|
||||
};
|
||||
|
||||
i2ccontrol@1760 {
|
||||
compatible = "fsl,mpc5121-i2c-ctrl";
|
||||
reg = <0x1760 0x8>;
|
||||
};
|
||||
|
||||
/* MPC5200B based board */
|
||||
i2c@3d00 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "fsl,mpc5200b-i2c","fsl,mpc5200-i2c","fsl-i2c";
|
||||
reg = <0x3d00 0x40>;
|
||||
interrupts = <2 15 0>;
|
||||
interrupt-parent = <&mpc5200_pic>;
|
||||
fsl,preserve-clocking;
|
||||
};
|
||||
|
||||
/* MPC8544 base board */
|
||||
i2c@3100 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "fsl,mpc8544-i2c", "fsl-i2c";
|
||||
reg = <0x3100 0x100>;
|
||||
interrupts = <43 2>;
|
||||
interrupt-parent = <&mpic>;
|
||||
clock-frequency = <400000>;
|
||||
fsl,timeout = <10000>;
|
||||
};
|
91
Documentation/devicetree/bindings/i2c/i2c-mpc.yaml
Normal file
91
Documentation/devicetree/bindings/i2c/i2c-mpc.yaml
Normal file
@ -0,0 +1,91 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/i2c/i2c-mpc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: I2C-Bus adapter for MPC824x/83xx/85xx/86xx/512x/52xx SoCs
|
||||
|
||||
maintainers:
|
||||
- Chris Packham <chris.packham@alliedtelesis.co.nz>
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/i2c/i2c-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- mpc5200-i2c
|
||||
- fsl,mpc5200-i2c
|
||||
- fsl,mpc5121-i2c
|
||||
- fsl,mpc8313-i2c
|
||||
- fsl,mpc8543-i2c
|
||||
- fsl,mpc8544-i2c
|
||||
- const: fsl-i2c
|
||||
- items:
|
||||
- const: fsl,mpc5200b-i2c
|
||||
- const: fsl,mpc5200-i2c
|
||||
- const: fsl-i2c
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
fsl,preserve-clocking:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description: |
|
||||
if defined, the clock settings from the bootloader are
|
||||
preserved (not touched)
|
||||
|
||||
fsl,timeout:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: |
|
||||
I2C bus timeout in microseconds
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
/* MPC5121 based board */
|
||||
i2c@1740 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "fsl,mpc5121-i2c", "fsl-i2c";
|
||||
reg = <0x1740 0x20>;
|
||||
interrupts = <11 0x8>;
|
||||
interrupt-parent = <&ipic>;
|
||||
clock-frequency = <100000>;
|
||||
};
|
||||
|
||||
/* MPC5200B based board */
|
||||
i2c@3d00 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "fsl,mpc5200b-i2c", "fsl,mpc5200-i2c", "fsl-i2c";
|
||||
reg = <0x3d00 0x40>;
|
||||
interrupts = <2 15 0>;
|
||||
interrupt-parent = <&mpc5200_pic>;
|
||||
fsl,preserve-clocking;
|
||||
};
|
||||
|
||||
/* MPC8544 base board */
|
||||
i2c@3100 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "fsl,mpc8544-i2c", "fsl-i2c";
|
||||
reg = <0x3100 0x100>;
|
||||
interrupts = <43 2>;
|
||||
interrupt-parent = <&mpic>;
|
||||
clock-frequency = <400000>;
|
||||
fsl,timeout = <10000>;
|
||||
};
|
||||
...
|
19
MAINTAINERS
19
MAINTAINERS
@ -4662,6 +4662,11 @@ F: drivers/counter/
|
||||
F: include/linux/counter.h
|
||||
F: include/linux/counter_enum.h
|
||||
|
||||
CP2615 I2C DRIVER
|
||||
M: Bence Csókás <bence98@sch.bme.hu>
|
||||
S: Maintained
|
||||
F: drivers/i2c/busses/i2c-cp2615.c
|
||||
|
||||
CPMAC ETHERNET DRIVER
|
||||
M: Florian Fainelli <f.fainelli@gmail.com>
|
||||
L: netdev@vger.kernel.org
|
||||
@ -7235,6 +7240,13 @@ S: Maintained
|
||||
F: Documentation/devicetree/bindings/i2c/i2c-imx-lpi2c.yaml
|
||||
F: drivers/i2c/busses/i2c-imx-lpi2c.c
|
||||
|
||||
FREESCALE MPC I2C DRIVER
|
||||
M: Chris Packham <chris.packham@alliedtelesis.co.nz>
|
||||
L: linux-i2c@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/i2c/i2c-mpc.yaml
|
||||
F: drivers/i2c/busses/i2c-mpc.c
|
||||
|
||||
FREESCALE QORIQ DPAA ETHERNET DRIVER
|
||||
M: Madalin Bucur <madalin.bucur@nxp.com>
|
||||
L: netdev@vger.kernel.org
|
||||
@ -8157,6 +8169,13 @@ F: drivers/crypto/hisilicon/hpre/hpre.h
|
||||
F: drivers/crypto/hisilicon/hpre/hpre_crypto.c
|
||||
F: drivers/crypto/hisilicon/hpre/hpre_main.c
|
||||
|
||||
HISILICON I2C CONTROLLER DRIVER
|
||||
M: Yicong Yang <yangyicong@hisilicon.com>
|
||||
L: linux-i2c@vger.kernel.org
|
||||
S: Maintained
|
||||
W: https://www.hisilicon.com
|
||||
F: drivers/i2c/busses/i2c-hisi.c
|
||||
|
||||
HISILICON LPC BUS DRIVER
|
||||
M: john.garry@huawei.com
|
||||
S: Maintained
|
||||
|
@ -454,6 +454,10 @@ static const struct property_entry da830_evm_i2c_eeprom_properties[] = {
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node da830_evm_i2c_eeprom_node = {
|
||||
.properties = da830_evm_i2c_eeprom_properties,
|
||||
};
|
||||
|
||||
static int __init da830_evm_ui_expander_setup(struct i2c_client *client,
|
||||
int gpio, unsigned ngpio, void *context)
|
||||
{
|
||||
@ -485,7 +489,7 @@ static struct pcf857x_platform_data __initdata da830_evm_ui_expander_info = {
|
||||
static struct i2c_board_info __initdata da830_evm_i2c_devices[] = {
|
||||
{
|
||||
I2C_BOARD_INFO("24c256", 0x50),
|
||||
.properties = da830_evm_i2c_eeprom_properties,
|
||||
.swnode = &da830_evm_i2c_eeprom_node,
|
||||
},
|
||||
{
|
||||
I2C_BOARD_INFO("tlv320aic3x", 0x18),
|
||||
|
@ -232,10 +232,14 @@ static const struct property_entry eeprom_properties[] = {
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node eeprom_node = {
|
||||
.properties = eeprom_properties,
|
||||
};
|
||||
|
||||
static struct i2c_board_info i2c_info[] = {
|
||||
{
|
||||
I2C_BOARD_INFO("24c256", 0x50),
|
||||
.properties = eeprom_properties,
|
||||
.swnode = &eeprom_node,
|
||||
},
|
||||
{
|
||||
I2C_BOARD_INFO("tlv320aic3x", 0x18),
|
||||
|
@ -541,6 +541,10 @@ static const struct property_entry eeprom_properties[] = {
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node eeprom_node = {
|
||||
.properties = eeprom_properties,
|
||||
};
|
||||
|
||||
/*
|
||||
* MSP430 supports RTC, card detection, input from IR remote, and
|
||||
* a bit more. It triggers interrupts on GPIO(7) from pressing
|
||||
@ -647,7 +651,7 @@ static struct i2c_board_info __initdata i2c_info[] = {
|
||||
},
|
||||
{
|
||||
I2C_BOARD_INFO("24c256", 0x50),
|
||||
.properties = eeprom_properties,
|
||||
.swnode = &eeprom_node,
|
||||
},
|
||||
{
|
||||
I2C_BOARD_INFO("tlv320aic33", 0x1b),
|
||||
|
@ -362,6 +362,10 @@ static const struct property_entry eeprom_properties[] = {
|
||||
PROPERTY_ENTRY_U32("pagesize", 64),
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node eeprom_node = {
|
||||
.properties = eeprom_properties,
|
||||
};
|
||||
#endif
|
||||
|
||||
static u8 dm646x_iis_serializer_direction[] = {
|
||||
@ -430,7 +434,7 @@ static void evm_init_cpld(void)
|
||||
static struct i2c_board_info __initdata i2c_info[] = {
|
||||
{
|
||||
I2C_BOARD_INFO("24c256", 0x50),
|
||||
.properties = eeprom_properties,
|
||||
.swnode = &eeprom_node,
|
||||
},
|
||||
{
|
||||
I2C_BOARD_INFO("pcf8574a", 0x38),
|
||||
|
@ -197,6 +197,10 @@ static const struct property_entry mityomapl138_fd_chip_properties[] = {
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node mityomapl138_fd_chip_node = {
|
||||
.properties = mityomapl138_fd_chip_properties,
|
||||
};
|
||||
|
||||
static struct davinci_i2c_platform_data mityomap_i2c_0_pdata = {
|
||||
.bus_freq = 100, /* kHz */
|
||||
.bus_delay = 0, /* usec */
|
||||
@ -323,7 +327,7 @@ static struct i2c_board_info __initdata mityomap_tps65023_info[] = {
|
||||
},
|
||||
{
|
||||
I2C_BOARD_INFO("24c02", 0x50),
|
||||
.properties = mityomapl138_fd_chip_properties,
|
||||
.swnode = &mityomapl138_fd_chip_node,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -84,10 +84,14 @@ static const struct property_entry eeprom_properties[] = {
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node eeprom_node = {
|
||||
.properties = eeprom_properties,
|
||||
};
|
||||
|
||||
static struct i2c_board_info __initdata i2c_info[] = {
|
||||
{
|
||||
I2C_BOARD_INFO("24c64", 0x50),
|
||||
.properties = eeprom_properties,
|
||||
.swnode = &eeprom_node,
|
||||
},
|
||||
/* Other I2C devices:
|
||||
* MSP430, addr 0x23 (not used)
|
||||
|
@ -332,11 +332,15 @@ static const struct property_entry mistral_at24_properties[] = {
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node mistral_at24_node = {
|
||||
.properties = mistral_at24_properties,
|
||||
};
|
||||
|
||||
static struct i2c_board_info __initdata mistral_i2c_board_info[] = {
|
||||
{
|
||||
/* NOTE: powered from LCD supply */
|
||||
I2C_BOARD_INFO("24c04", 0x50),
|
||||
.properties = mistral_at24_properties,
|
||||
.swnode = &mistral_at24_node,
|
||||
},
|
||||
/* TODO when driver support is ready:
|
||||
* - optionally ov9640 camera sensor at 0x30
|
||||
|
@ -794,6 +794,10 @@ static const struct property_entry pca9500_eeprom_properties[] = {
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node pca9500_eeprom_node = {
|
||||
.properties = pca9500_eeprom_properties,
|
||||
};
|
||||
|
||||
/**
|
||||
* stargate2_reset_bluetooth() reset the bluecore to ensure consistent state
|
||||
**/
|
||||
@ -929,7 +933,7 @@ static struct i2c_board_info __initdata stargate2_i2c_board_info[] = {
|
||||
}, {
|
||||
.type = "24c02",
|
||||
.addr = 0x57,
|
||||
.properties = pca9500_eeprom_properties,
|
||||
.swnode = &pca9500_eeprom_node,
|
||||
}, {
|
||||
.type = "max1238",
|
||||
.addr = 0x35,
|
||||
|
@ -542,10 +542,14 @@ static const struct property_entry mini2440_at24_properties[] = {
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node mini2440_at24_node = {
|
||||
.properties = mini2440_at24_properties,
|
||||
};
|
||||
|
||||
static struct i2c_board_info mini2440_i2c_devs[] __initdata = {
|
||||
{
|
||||
I2C_BOARD_INFO("24c08", 0x50),
|
||||
.properties = mini2440_at24_properties,
|
||||
.swnode = &mini2440_at24_node,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -645,6 +645,16 @@ config I2C_HIGHLANDER
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-highlander.
|
||||
|
||||
config I2C_HISI
|
||||
tristate "HiSilicon I2C controller"
|
||||
depends on ARM64 || COMPILE_TEST
|
||||
help
|
||||
Say Y here if you want to have Hisilicon I2C controller support
|
||||
available on the Kunpeng Server.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-hisi.
|
||||
|
||||
config I2C_IBM_IIC
|
||||
tristate "IBM PPC 4xx on-chip I2C interface"
|
||||
depends on 4xx
|
||||
@ -1199,6 +1209,16 @@ config I2C_DLN2
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-dln2.
|
||||
|
||||
config I2C_CP2615
|
||||
tristate "Silicon Labs CP2615 USB sound card and I2C adapter"
|
||||
depends on USB
|
||||
help
|
||||
If you say yes to this option, support will be included for Silicon
|
||||
Labs CP2615's I2C interface.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-cp2615.
|
||||
|
||||
config I2C_PARPORT
|
||||
tristate "Parallel port adapter"
|
||||
depends on PARPORT
|
||||
|
@ -63,6 +63,7 @@ obj-$(CONFIG_I2C_EMEV2) += i2c-emev2.o
|
||||
obj-$(CONFIG_I2C_EXYNOS5) += i2c-exynos5.o
|
||||
obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o
|
||||
obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o
|
||||
obj-$(CONFIG_I2C_HISI) += i2c-hisi.o
|
||||
obj-$(CONFIG_I2C_HIX5HD2) += i2c-hix5hd2.o
|
||||
obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o
|
||||
obj-$(CONFIG_I2C_IMG) += i2c-img-scb.o
|
||||
@ -123,6 +124,7 @@ obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o
|
||||
# External I2C/SMBus adapter drivers
|
||||
obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
|
||||
obj-$(CONFIG_I2C_DLN2) += i2c-dln2.o
|
||||
obj-$(CONFIG_I2C_CP2615) += i2c-cp2615.o
|
||||
obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o
|
||||
obj-$(CONFIG_I2C_ROBOTFUZZ_OSIF) += i2c-robotfuzz-osif.o
|
||||
obj-$(CONFIG_I2C_TAOS_EVM) += i2c-taos-evm.o
|
||||
|
@ -186,9 +186,9 @@ static int amd_ec_write(struct amd_smbus *smbus, unsigned char address,
|
||||
#define AMD_SMB_PRTCL_PEC 0x80
|
||||
|
||||
|
||||
static s32 amd8111_access(struct i2c_adapter * adap, u16 addr,
|
||||
static s32 amd8111_access(struct i2c_adapter *adap, u16 addr,
|
||||
unsigned short flags, char read_write, u8 command, int size,
|
||||
union i2c_smbus_data * data)
|
||||
union i2c_smbus_data *data)
|
||||
{
|
||||
struct amd_smbus *smbus = adap->algo_data;
|
||||
unsigned char protocol, len, pec, temp[2];
|
||||
@ -199,130 +199,130 @@ static s32 amd8111_access(struct i2c_adapter * adap, u16 addr,
|
||||
pec = (flags & I2C_CLIENT_PEC) ? AMD_SMB_PRTCL_PEC : 0;
|
||||
|
||||
switch (size) {
|
||||
case I2C_SMBUS_QUICK:
|
||||
protocol |= AMD_SMB_PRTCL_QUICK;
|
||||
read_write = I2C_SMBUS_WRITE;
|
||||
break;
|
||||
case I2C_SMBUS_QUICK:
|
||||
protocol |= AMD_SMB_PRTCL_QUICK;
|
||||
read_write = I2C_SMBUS_WRITE;
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_BYTE:
|
||||
if (read_write == I2C_SMBUS_WRITE) {
|
||||
status = amd_ec_write(smbus, AMD_SMB_CMD,
|
||||
command);
|
||||
if (status)
|
||||
return status;
|
||||
}
|
||||
protocol |= AMD_SMB_PRTCL_BYTE;
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_BYTE_DATA:
|
||||
status = amd_ec_write(smbus, AMD_SMB_CMD, command);
|
||||
case I2C_SMBUS_BYTE:
|
||||
if (read_write == I2C_SMBUS_WRITE) {
|
||||
status = amd_ec_write(smbus, AMD_SMB_CMD,
|
||||
command);
|
||||
if (status)
|
||||
return status;
|
||||
if (read_write == I2C_SMBUS_WRITE) {
|
||||
status = amd_ec_write(smbus, AMD_SMB_DATA,
|
||||
data->byte);
|
||||
if (status)
|
||||
return status;
|
||||
}
|
||||
protocol |= AMD_SMB_PRTCL_BYTE_DATA;
|
||||
break;
|
||||
}
|
||||
protocol |= AMD_SMB_PRTCL_BYTE;
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_WORD_DATA:
|
||||
status = amd_ec_write(smbus, AMD_SMB_CMD, command);
|
||||
if (status)
|
||||
return status;
|
||||
if (read_write == I2C_SMBUS_WRITE) {
|
||||
status = amd_ec_write(smbus, AMD_SMB_DATA,
|
||||
data->word & 0xff);
|
||||
if (status)
|
||||
return status;
|
||||
status = amd_ec_write(smbus, AMD_SMB_DATA + 1,
|
||||
data->word >> 8);
|
||||
if (status)
|
||||
return status;
|
||||
}
|
||||
protocol |= AMD_SMB_PRTCL_WORD_DATA | pec;
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_BLOCK_DATA:
|
||||
status = amd_ec_write(smbus, AMD_SMB_CMD, command);
|
||||
if (status)
|
||||
return status;
|
||||
if (read_write == I2C_SMBUS_WRITE) {
|
||||
len = min_t(u8, data->block[0],
|
||||
I2C_SMBUS_BLOCK_MAX);
|
||||
status = amd_ec_write(smbus, AMD_SMB_BCNT, len);
|
||||
if (status)
|
||||
return status;
|
||||
for (i = 0; i < len; i++) {
|
||||
status =
|
||||
amd_ec_write(smbus, AMD_SMB_DATA + i,
|
||||
data->block[i + 1]);
|
||||
if (status)
|
||||
return status;
|
||||
}
|
||||
}
|
||||
protocol |= AMD_SMB_PRTCL_BLOCK_DATA | pec;
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_I2C_BLOCK_DATA:
|
||||
len = min_t(u8, data->block[0],
|
||||
I2C_SMBUS_BLOCK_MAX);
|
||||
status = amd_ec_write(smbus, AMD_SMB_CMD, command);
|
||||
if (status)
|
||||
return status;
|
||||
status = amd_ec_write(smbus, AMD_SMB_BCNT, len);
|
||||
if (status)
|
||||
return status;
|
||||
if (read_write == I2C_SMBUS_WRITE)
|
||||
for (i = 0; i < len; i++) {
|
||||
status =
|
||||
amd_ec_write(smbus, AMD_SMB_DATA + i,
|
||||
data->block[i + 1]);
|
||||
if (status)
|
||||
return status;
|
||||
}
|
||||
protocol |= AMD_SMB_PRTCL_I2C_BLOCK_DATA;
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_PROC_CALL:
|
||||
status = amd_ec_write(smbus, AMD_SMB_CMD, command);
|
||||
if (status)
|
||||
return status;
|
||||
case I2C_SMBUS_BYTE_DATA:
|
||||
status = amd_ec_write(smbus, AMD_SMB_CMD, command);
|
||||
if (status)
|
||||
return status;
|
||||
if (read_write == I2C_SMBUS_WRITE) {
|
||||
status = amd_ec_write(smbus, AMD_SMB_DATA,
|
||||
data->word & 0xff);
|
||||
data->byte);
|
||||
if (status)
|
||||
return status;
|
||||
}
|
||||
protocol |= AMD_SMB_PRTCL_BYTE_DATA;
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_WORD_DATA:
|
||||
status = amd_ec_write(smbus, AMD_SMB_CMD, command);
|
||||
if (status)
|
||||
return status;
|
||||
if (read_write == I2C_SMBUS_WRITE) {
|
||||
status = amd_ec_write(smbus, AMD_SMB_DATA,
|
||||
data->word & 0xff);
|
||||
if (status)
|
||||
return status;
|
||||
status = amd_ec_write(smbus, AMD_SMB_DATA + 1,
|
||||
data->word >> 8);
|
||||
data->word >> 8);
|
||||
if (status)
|
||||
return status;
|
||||
protocol = AMD_SMB_PRTCL_PROC_CALL | pec;
|
||||
read_write = I2C_SMBUS_READ;
|
||||
break;
|
||||
}
|
||||
protocol |= AMD_SMB_PRTCL_WORD_DATA | pec;
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_BLOCK_PROC_CALL:
|
||||
case I2C_SMBUS_BLOCK_DATA:
|
||||
status = amd_ec_write(smbus, AMD_SMB_CMD, command);
|
||||
if (status)
|
||||
return status;
|
||||
if (read_write == I2C_SMBUS_WRITE) {
|
||||
len = min_t(u8, data->block[0],
|
||||
I2C_SMBUS_BLOCK_MAX - 1);
|
||||
status = amd_ec_write(smbus, AMD_SMB_CMD, command);
|
||||
if (status)
|
||||
return status;
|
||||
I2C_SMBUS_BLOCK_MAX);
|
||||
status = amd_ec_write(smbus, AMD_SMB_BCNT, len);
|
||||
if (status)
|
||||
return status;
|
||||
for (i = 0; i < len; i++) {
|
||||
status = amd_ec_write(smbus, AMD_SMB_DATA + i,
|
||||
data->block[i + 1]);
|
||||
status =
|
||||
amd_ec_write(smbus, AMD_SMB_DATA + i,
|
||||
data->block[i + 1]);
|
||||
if (status)
|
||||
return status;
|
||||
}
|
||||
protocol = AMD_SMB_PRTCL_BLOCK_PROC_CALL | pec;
|
||||
read_write = I2C_SMBUS_READ;
|
||||
break;
|
||||
}
|
||||
protocol |= AMD_SMB_PRTCL_BLOCK_DATA | pec;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
|
||||
return -EOPNOTSUPP;
|
||||
case I2C_SMBUS_I2C_BLOCK_DATA:
|
||||
len = min_t(u8, data->block[0],
|
||||
I2C_SMBUS_BLOCK_MAX);
|
||||
status = amd_ec_write(smbus, AMD_SMB_CMD, command);
|
||||
if (status)
|
||||
return status;
|
||||
status = amd_ec_write(smbus, AMD_SMB_BCNT, len);
|
||||
if (status)
|
||||
return status;
|
||||
if (read_write == I2C_SMBUS_WRITE)
|
||||
for (i = 0; i < len; i++) {
|
||||
status =
|
||||
amd_ec_write(smbus, AMD_SMB_DATA + i,
|
||||
data->block[i + 1]);
|
||||
if (status)
|
||||
return status;
|
||||
}
|
||||
protocol |= AMD_SMB_PRTCL_I2C_BLOCK_DATA;
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_PROC_CALL:
|
||||
status = amd_ec_write(smbus, AMD_SMB_CMD, command);
|
||||
if (status)
|
||||
return status;
|
||||
status = amd_ec_write(smbus, AMD_SMB_DATA,
|
||||
data->word & 0xff);
|
||||
if (status)
|
||||
return status;
|
||||
status = amd_ec_write(smbus, AMD_SMB_DATA + 1,
|
||||
data->word >> 8);
|
||||
if (status)
|
||||
return status;
|
||||
protocol = AMD_SMB_PRTCL_PROC_CALL | pec;
|
||||
read_write = I2C_SMBUS_READ;
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_BLOCK_PROC_CALL:
|
||||
len = min_t(u8, data->block[0],
|
||||
I2C_SMBUS_BLOCK_MAX - 1);
|
||||
status = amd_ec_write(smbus, AMD_SMB_CMD, command);
|
||||
if (status)
|
||||
return status;
|
||||
status = amd_ec_write(smbus, AMD_SMB_BCNT, len);
|
||||
if (status)
|
||||
return status;
|
||||
for (i = 0; i < len; i++) {
|
||||
status = amd_ec_write(smbus, AMD_SMB_DATA + i,
|
||||
data->block[i + 1]);
|
||||
if (status)
|
||||
return status;
|
||||
}
|
||||
protocol = AMD_SMB_PRTCL_BLOCK_PROC_CALL | pec;
|
||||
read_write = I2C_SMBUS_READ;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
status = amd_ec_write(smbus, AMD_SMB_ADDR, addr << 1);
|
||||
@ -357,40 +357,40 @@ static s32 amd8111_access(struct i2c_adapter * adap, u16 addr,
|
||||
return 0;
|
||||
|
||||
switch (size) {
|
||||
case I2C_SMBUS_BYTE:
|
||||
case I2C_SMBUS_BYTE_DATA:
|
||||
status = amd_ec_read(smbus, AMD_SMB_DATA, &data->byte);
|
||||
if (status)
|
||||
return status;
|
||||
break;
|
||||
case I2C_SMBUS_BYTE:
|
||||
case I2C_SMBUS_BYTE_DATA:
|
||||
status = amd_ec_read(smbus, AMD_SMB_DATA, &data->byte);
|
||||
if (status)
|
||||
return status;
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_WORD_DATA:
|
||||
case I2C_SMBUS_PROC_CALL:
|
||||
status = amd_ec_read(smbus, AMD_SMB_DATA, temp + 0);
|
||||
if (status)
|
||||
return status;
|
||||
status = amd_ec_read(smbus, AMD_SMB_DATA + 1, temp + 1);
|
||||
if (status)
|
||||
return status;
|
||||
data->word = (temp[1] << 8) | temp[0];
|
||||
break;
|
||||
case I2C_SMBUS_WORD_DATA:
|
||||
case I2C_SMBUS_PROC_CALL:
|
||||
status = amd_ec_read(smbus, AMD_SMB_DATA, temp + 0);
|
||||
if (status)
|
||||
return status;
|
||||
status = amd_ec_read(smbus, AMD_SMB_DATA + 1, temp + 1);
|
||||
if (status)
|
||||
return status;
|
||||
data->word = (temp[1] << 8) | temp[0];
|
||||
break;
|
||||
|
||||
case I2C_SMBUS_BLOCK_DATA:
|
||||
case I2C_SMBUS_BLOCK_PROC_CALL:
|
||||
status = amd_ec_read(smbus, AMD_SMB_BCNT, &len);
|
||||
case I2C_SMBUS_BLOCK_DATA:
|
||||
case I2C_SMBUS_BLOCK_PROC_CALL:
|
||||
status = amd_ec_read(smbus, AMD_SMB_BCNT, &len);
|
||||
if (status)
|
||||
return status;
|
||||
len = min_t(u8, len, I2C_SMBUS_BLOCK_MAX);
|
||||
fallthrough;
|
||||
case I2C_SMBUS_I2C_BLOCK_DATA:
|
||||
for (i = 0; i < len; i++) {
|
||||
status = amd_ec_read(smbus, AMD_SMB_DATA + i,
|
||||
data->block + i + 1);
|
||||
if (status)
|
||||
return status;
|
||||
len = min_t(u8, len, I2C_SMBUS_BLOCK_MAX);
|
||||
fallthrough;
|
||||
case I2C_SMBUS_I2C_BLOCK_DATA:
|
||||
for (i = 0; i < len; i++) {
|
||||
status = amd_ec_read(smbus, AMD_SMB_DATA + i,
|
||||
data->block + i + 1);
|
||||
if (status)
|
||||
return status;
|
||||
}
|
||||
data->block[0] = len;
|
||||
break;
|
||||
}
|
||||
data->block[0] = len;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -22,7 +22,6 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#define N_DATA_REGS 8
|
||||
|
||||
|
@ -789,7 +789,7 @@ static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||
bool change_role = false;
|
||||
#endif
|
||||
|
||||
ret = pm_runtime_get_sync(id->dev);
|
||||
ret = pm_runtime_resume_and_get(id->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -911,7 +911,7 @@ static int cdns_reg_slave(struct i2c_client *slave)
|
||||
if (slave->flags & I2C_CLIENT_TEN)
|
||||
return -EAFNOSUPPORT;
|
||||
|
||||
ret = pm_runtime_get_sync(id->dev);
|
||||
ret = pm_runtime_resume_and_get(id->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -1200,7 +1200,10 @@ static int cdns_i2c_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(id->membase))
|
||||
return PTR_ERR(id->membase);
|
||||
|
||||
id->irq = platform_get_irq(pdev, 0);
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
id->irq = ret;
|
||||
|
||||
id->adap.owner = THIS_MODULE;
|
||||
id->adap.dev.of_node = pdev->dev.of_node;
|
||||
|
@ -280,6 +280,10 @@ static const struct property_entry bq24190_props[] = {
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node bq24190_node = {
|
||||
.properties = bq24190_props,
|
||||
};
|
||||
|
||||
static struct regulator_consumer_supply fusb302_consumer = {
|
||||
.supply = "vbus",
|
||||
/* Must match fusb302 dev_name in intel_cht_int33fe.c */
|
||||
@ -308,7 +312,7 @@ static int cht_wc_i2c_adap_i2c_probe(struct platform_device *pdev)
|
||||
.type = "bq24190",
|
||||
.addr = 0x6b,
|
||||
.dev_name = "bq24190",
|
||||
.properties = bq24190_props,
|
||||
.swnode = &bq24190_node,
|
||||
.platform_data = &bq24190_pdata,
|
||||
};
|
||||
int ret, reg, irq;
|
||||
|
330
drivers/i2c/busses/i2c-cp2615.c
Normal file
330
drivers/i2c/busses/i2c-cp2615.c
Normal file
@ -0,0 +1,330 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* i2c support for Silicon Labs' CP2615 Digital Audio Bridge
|
||||
*
|
||||
* (c) 2021, Bence Csókás <bence98@sch.bme.hu>
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/usb.h>
|
||||
|
||||
/** CP2615 I/O Protocol implementation */
|
||||
|
||||
#define CP2615_VID 0x10c4
|
||||
#define CP2615_PID 0xeac1
|
||||
|
||||
#define IOP_EP_IN 0x82
|
||||
#define IOP_EP_OUT 0x02
|
||||
#define IOP_IFN 1
|
||||
#define IOP_ALTSETTING 2
|
||||
|
||||
#define MAX_IOP_SIZE 64
|
||||
#define MAX_IOP_PAYLOAD_SIZE (MAX_IOP_SIZE - 6)
|
||||
#define MAX_I2C_SIZE (MAX_IOP_PAYLOAD_SIZE - 4)
|
||||
|
||||
enum cp2615_iop_msg_type {
|
||||
iop_GetAccessoryInfo = 0xD100,
|
||||
iop_AccessoryInfo = 0xA100,
|
||||
iop_GetPortConfiguration = 0xD203,
|
||||
iop_PortConfiguration = 0xA203,
|
||||
iop_DoI2cTransfer = 0xD400,
|
||||
iop_I2cTransferResult = 0xA400,
|
||||
iop_GetSerialState = 0xD501,
|
||||
iop_SerialState = 0xA501
|
||||
};
|
||||
|
||||
struct __packed cp2615_iop_msg {
|
||||
__be16 preamble, length, msg;
|
||||
u8 data[MAX_IOP_PAYLOAD_SIZE];
|
||||
};
|
||||
|
||||
#define PART_ID_A01 0x1400
|
||||
#define PART_ID_A02 0x1500
|
||||
|
||||
struct __packed cp2615_iop_accessory_info {
|
||||
__be16 part_id, option_id, proto_ver;
|
||||
};
|
||||
|
||||
struct __packed cp2615_i2c_transfer {
|
||||
u8 tag, i2caddr, read_len, write_len;
|
||||
u8 data[MAX_I2C_SIZE];
|
||||
};
|
||||
|
||||
/* Possible values for struct cp2615_i2c_transfer_result.status */
|
||||
enum cp2615_i2c_status {
|
||||
/* Writing to the internal EEPROM failed, because it is locked */
|
||||
CP2615_CFG_LOCKED = -6,
|
||||
/* read_len or write_len out of range */
|
||||
CP2615_INVALID_PARAM = -4,
|
||||
/* I2C slave did not ACK in time */
|
||||
CP2615_TIMEOUT,
|
||||
/* I2C bus busy */
|
||||
CP2615_BUS_BUSY,
|
||||
/* I2C bus error (ie. device NAK'd the request) */
|
||||
CP2615_BUS_ERROR,
|
||||
CP2615_SUCCESS
|
||||
};
|
||||
|
||||
struct __packed cp2615_i2c_transfer_result {
|
||||
u8 tag, i2caddr;
|
||||
s8 status;
|
||||
u8 read_len;
|
||||
u8 data[MAX_I2C_SIZE];
|
||||
};
|
||||
|
||||
static int cp2615_init_iop_msg(struct cp2615_iop_msg *ret, enum cp2615_iop_msg_type msg,
|
||||
const void *data, size_t data_len)
|
||||
{
|
||||
if (data_len > MAX_IOP_PAYLOAD_SIZE)
|
||||
return -EFBIG;
|
||||
|
||||
if (!ret)
|
||||
return -EINVAL;
|
||||
|
||||
ret->preamble = 0x2A2A;
|
||||
ret->length = htons(data_len + 6);
|
||||
ret->msg = htons(msg);
|
||||
if (data && data_len)
|
||||
memcpy(&ret->data, data, data_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cp2615_init_i2c_msg(struct cp2615_iop_msg *ret, const struct cp2615_i2c_transfer *data)
|
||||
{
|
||||
return cp2615_init_iop_msg(ret, iop_DoI2cTransfer, data, 4 + data->write_len);
|
||||
}
|
||||
|
||||
/* Translates status codes to Linux errno's */
|
||||
static int cp2615_check_status(enum cp2615_i2c_status status)
|
||||
{
|
||||
switch (status) {
|
||||
case CP2615_SUCCESS:
|
||||
return 0;
|
||||
case CP2615_BUS_ERROR:
|
||||
return -ENXIO;
|
||||
case CP2615_BUS_BUSY:
|
||||
return -EAGAIN;
|
||||
case CP2615_TIMEOUT:
|
||||
return -ETIMEDOUT;
|
||||
case CP2615_INVALID_PARAM:
|
||||
return -EINVAL;
|
||||
case CP2615_CFG_LOCKED:
|
||||
return -EPERM;
|
||||
}
|
||||
/* Unknown error code */
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
/** Driver code */
|
||||
|
||||
static int
|
||||
cp2615_i2c_send(struct usb_interface *usbif, struct cp2615_i2c_transfer *i2c_w)
|
||||
{
|
||||
struct cp2615_iop_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
|
||||
struct usb_device *usbdev = interface_to_usbdev(usbif);
|
||||
int res = cp2615_init_i2c_msg(msg, i2c_w);
|
||||
|
||||
if (!res)
|
||||
res = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, IOP_EP_OUT),
|
||||
msg, ntohs(msg->length), NULL, 0);
|
||||
kfree(msg);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int
|
||||
cp2615_i2c_recv(struct usb_interface *usbif, unsigned char tag, void *buf)
|
||||
{
|
||||
struct cp2615_iop_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
|
||||
struct cp2615_i2c_transfer_result *i2c_r = (struct cp2615_i2c_transfer_result *)&msg->data;
|
||||
struct usb_device *usbdev = interface_to_usbdev(usbif);
|
||||
int res = usb_bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, IOP_EP_IN),
|
||||
msg, sizeof(struct cp2615_iop_msg), NULL, 0);
|
||||
|
||||
if (res < 0) {
|
||||
kfree(msg);
|
||||
return res;
|
||||
}
|
||||
|
||||
if (msg->msg != htons(iop_I2cTransferResult) || i2c_r->tag != tag) {
|
||||
kfree(msg);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
res = cp2615_check_status(i2c_r->status);
|
||||
if (!res)
|
||||
memcpy(buf, &i2c_r->data, i2c_r->read_len);
|
||||
|
||||
kfree(msg);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Checks if the IOP is functional by querying the part's ID */
|
||||
static int cp2615_check_iop(struct usb_interface *usbif)
|
||||
{
|
||||
struct cp2615_iop_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL);
|
||||
struct cp2615_iop_accessory_info *info = (struct cp2615_iop_accessory_info *)&msg->data;
|
||||
struct usb_device *usbdev = interface_to_usbdev(usbif);
|
||||
int res = cp2615_init_iop_msg(msg, iop_GetAccessoryInfo, NULL, 0);
|
||||
|
||||
if (res)
|
||||
goto out;
|
||||
|
||||
res = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, IOP_EP_OUT),
|
||||
msg, ntohs(msg->length), NULL, 0);
|
||||
if (res)
|
||||
goto out;
|
||||
|
||||
res = usb_bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, IOP_EP_IN),
|
||||
msg, sizeof(struct cp2615_iop_msg), NULL, 0);
|
||||
if (res)
|
||||
goto out;
|
||||
|
||||
if (msg->msg != htons(iop_AccessoryInfo)) {
|
||||
res = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (ntohs(info->part_id)) {
|
||||
case PART_ID_A01:
|
||||
dev_dbg(&usbif->dev, "Found A01 part. (WARNING: errata exists!)\n");
|
||||
break;
|
||||
case PART_ID_A02:
|
||||
dev_dbg(&usbif->dev, "Found good A02 part.\n");
|
||||
break;
|
||||
default:
|
||||
dev_warn(&usbif->dev, "Unknown part ID %04X\n", ntohs(info->part_id));
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(msg);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int
|
||||
cp2615_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
||||
{
|
||||
struct usb_interface *usbif = adap->algo_data;
|
||||
int i = 0, ret = 0;
|
||||
struct i2c_msg *msg;
|
||||
struct cp2615_i2c_transfer i2c_w = {0};
|
||||
|
||||
dev_dbg(&usbif->dev, "Doing %d I2C transactions\n", num);
|
||||
|
||||
for (; !ret && i < num; i++) {
|
||||
msg = &msgs[i];
|
||||
|
||||
i2c_w.tag = 0xdd;
|
||||
i2c_w.i2caddr = i2c_8bit_addr_from_msg(msg);
|
||||
if (msg->flags & I2C_M_RD) {
|
||||
i2c_w.read_len = msg->len;
|
||||
i2c_w.write_len = 0;
|
||||
} else {
|
||||
i2c_w.read_len = 0;
|
||||
i2c_w.write_len = msg->len;
|
||||
memcpy(&i2c_w.data, msg->buf, i2c_w.write_len);
|
||||
}
|
||||
ret = cp2615_i2c_send(usbif, &i2c_w);
|
||||
if (ret)
|
||||
break;
|
||||
ret = cp2615_i2c_recv(usbif, i2c_w.tag, msg->buf);
|
||||
}
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return i;
|
||||
}
|
||||
|
||||
static u32
|
||||
cp2615_i2c_func(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm cp2615_i2c_algo = {
|
||||
.master_xfer = cp2615_i2c_master_xfer,
|
||||
.functionality = cp2615_i2c_func,
|
||||
};
|
||||
|
||||
/*
|
||||
* This chip has some limitations: one is that the USB endpoint
|
||||
* can only receive 64 bytes/transfer, that leaves 54 bytes for
|
||||
* the I2C transfer. On top of that, EITHER read_len OR write_len
|
||||
* may be zero, but not both. If both are non-zero, the adapter
|
||||
* issues a write followed by a read. And the chip does not
|
||||
* support repeated START between the write and read phases.
|
||||
*/
|
||||
static struct i2c_adapter_quirks cp2615_i2c_quirks = {
|
||||
.max_write_len = MAX_I2C_SIZE,
|
||||
.max_read_len = MAX_I2C_SIZE,
|
||||
.flags = I2C_AQ_COMB_WRITE_THEN_READ | I2C_AQ_NO_ZERO_LEN | I2C_AQ_NO_REP_START,
|
||||
.max_comb_1st_msg_len = MAX_I2C_SIZE,
|
||||
.max_comb_2nd_msg_len = MAX_I2C_SIZE
|
||||
};
|
||||
|
||||
static void
|
||||
cp2615_i2c_remove(struct usb_interface *usbif)
|
||||
{
|
||||
struct i2c_adapter *adap = usb_get_intfdata(usbif);
|
||||
|
||||
usb_set_intfdata(usbif, NULL);
|
||||
i2c_del_adapter(adap);
|
||||
}
|
||||
|
||||
static int
|
||||
cp2615_i2c_probe(struct usb_interface *usbif, const struct usb_device_id *id)
|
||||
{
|
||||
int ret = 0;
|
||||
struct i2c_adapter *adap;
|
||||
struct usb_device *usbdev = interface_to_usbdev(usbif);
|
||||
|
||||
ret = usb_set_interface(usbdev, IOP_IFN, IOP_ALTSETTING);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = cp2615_check_iop(usbif);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
adap = devm_kzalloc(&usbif->dev, sizeof(struct i2c_adapter), GFP_KERNEL);
|
||||
if (!adap)
|
||||
return -ENOMEM;
|
||||
|
||||
strncpy(adap->name, usbdev->serial, sizeof(adap->name) - 1);
|
||||
adap->owner = THIS_MODULE;
|
||||
adap->dev.parent = &usbif->dev;
|
||||
adap->dev.of_node = usbif->dev.of_node;
|
||||
adap->timeout = HZ;
|
||||
adap->algo = &cp2615_i2c_algo;
|
||||
adap->quirks = &cp2615_i2c_quirks;
|
||||
adap->algo_data = usbif;
|
||||
|
||||
ret = i2c_add_adapter(adap);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
usb_set_intfdata(usbif, adap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct usb_device_id id_table[] = {
|
||||
{ USB_DEVICE_INTERFACE_NUMBER(CP2615_VID, CP2615_PID, IOP_IFN) },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, id_table);
|
||||
|
||||
static struct usb_driver cp2615_i2c_driver = {
|
||||
.name = "i2c-cp2615",
|
||||
.probe = cp2615_i2c_probe,
|
||||
.disconnect = cp2615_i2c_remove,
|
||||
.id_table = id_table,
|
||||
};
|
||||
|
||||
module_usb_driver(cp2615_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Bence Csókás <bence98@sch.bme.hu>");
|
||||
MODULE_DESCRIPTION("CP2615 I2C bus driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -150,6 +150,9 @@ int i2c_dw_init_regmap(struct dw_i2c_dev *dev)
|
||||
reg = readl(dev->base + DW_IC_COMP_TYPE);
|
||||
i2c_dw_release_lock(dev);
|
||||
|
||||
if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU)
|
||||
map_cfg.max_register = AMD_UCSI_INTR_REG;
|
||||
|
||||
if (reg == swab32(DW_IC_COMP_TYPE_VALUE)) {
|
||||
map_cfg.reg_read = dw_reg_read_swab;
|
||||
map_cfg.reg_write = dw_reg_write_swab;
|
||||
|
@ -295,8 +295,16 @@ struct dw_i2c_dev {
|
||||
|
||||
#define MODEL_MSCC_OCELOT BIT(8)
|
||||
#define MODEL_BAIKAL_BT1 BIT(9)
|
||||
#define MODEL_AMD_NAVI_GPU BIT(10)
|
||||
#define MODEL_MASK GENMASK(11, 8)
|
||||
|
||||
/*
|
||||
* Enable UCSI interrupt by writing 0xd at register
|
||||
* offset 0x474 specified in hardware specification.
|
||||
*/
|
||||
#define AMD_UCSI_INTR_REG 0x474
|
||||
#define AMD_UCSI_INTR_EN 0xd
|
||||
|
||||
int i2c_dw_init_regmap(struct dw_i2c_dev *dev);
|
||||
u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset);
|
||||
u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset);
|
||||
|
@ -23,6 +23,10 @@
|
||||
|
||||
#include "i2c-designware-core.h"
|
||||
|
||||
#define AMD_TIMEOUT_MIN_US 25
|
||||
#define AMD_TIMEOUT_MAX_US 250
|
||||
#define AMD_MASTERCFG_MASK GENMASK(15, 0)
|
||||
|
||||
static void i2c_dw_configure_fifo_master(struct dw_i2c_dev *dev)
|
||||
{
|
||||
/* Configure Tx/Rx FIFO threshold levels */
|
||||
@ -35,10 +39,10 @@ static void i2c_dw_configure_fifo_master(struct dw_i2c_dev *dev)
|
||||
|
||||
static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
|
||||
{
|
||||
const char *mode_str, *fp_str = "";
|
||||
u32 comp_param1;
|
||||
u32 sda_falling_time, scl_falling_time;
|
||||
struct i2c_timings *t = &dev->timings;
|
||||
const char *fp_str = "";
|
||||
u32 ic_clk;
|
||||
int ret;
|
||||
|
||||
@ -78,7 +82,7 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
|
||||
* difference is the timing parameter values since the registers are
|
||||
* the same.
|
||||
*/
|
||||
if (t->bus_freq_hz == 1000000) {
|
||||
if (t->bus_freq_hz == I2C_MAX_FAST_MODE_PLUS_FREQ) {
|
||||
/*
|
||||
* Check are Fast Mode Plus parameters available. Calculate
|
||||
* SCL timing parameters for Fast Mode Plus if not set.
|
||||
@ -154,22 +158,10 @@ static int i2c_dw_set_timings_master(struct dw_i2c_dev *dev)
|
||||
|
||||
ret = i2c_dw_set_sda_hold(dev);
|
||||
if (ret)
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
switch (dev->master_cfg & DW_IC_CON_SPEED_MASK) {
|
||||
case DW_IC_CON_SPEED_STD:
|
||||
mode_str = "Standard Mode";
|
||||
break;
|
||||
case DW_IC_CON_SPEED_HIGH:
|
||||
mode_str = "High Speed Mode";
|
||||
break;
|
||||
default:
|
||||
mode_str = "Fast Mode";
|
||||
}
|
||||
dev_dbg(dev->dev, "Bus speed: %s%s\n", mode_str, fp_str);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
dev_dbg(dev->dev, "Bus speed: %s\n", i2c_freq_mode_string(t->bus_freq_hz));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -260,6 +252,108 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
|
||||
regmap_write(dev->map, DW_IC_INTR_MASK, DW_IC_INTR_MASTER_MASK);
|
||||
}
|
||||
|
||||
static int i2c_dw_check_stopbit(struct dw_i2c_dev *dev)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read_poll_timeout(dev->map, DW_IC_INTR_STAT, val,
|
||||
!(val & DW_IC_INTR_STOP_DET),
|
||||
1100, 20000);
|
||||
if (ret)
|
||||
dev_err(dev->dev, "i2c timeout error %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int i2c_dw_status(struct dw_i2c_dev *dev)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = i2c_dw_wait_bus_not_busy(dev);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
return i2c_dw_check_stopbit(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initiate and continue master read/write transaction with polling
|
||||
* based transfer routine afterward write messages into the Tx buffer.
|
||||
*/
|
||||
static int amd_i2c_dw_xfer_quirk(struct i2c_adapter *adap, struct i2c_msg *msgs, int num_msgs)
|
||||
{
|
||||
struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
|
||||
int msg_wrt_idx, msg_itr_lmt, buf_len, data_idx;
|
||||
int cmd = 0, status;
|
||||
u8 *tx_buf;
|
||||
u32 val;
|
||||
|
||||
/*
|
||||
* In order to enable the interrupt for UCSI i.e. AMD NAVI GPU card,
|
||||
* it is mandatory to set the right value in specific register
|
||||
* (offset:0x474) as per the hardware IP specification.
|
||||
*/
|
||||
regmap_write(dev->map, AMD_UCSI_INTR_REG, AMD_UCSI_INTR_EN);
|
||||
|
||||
dev->msgs = msgs;
|
||||
dev->msgs_num = num_msgs;
|
||||
i2c_dw_xfer_init(dev);
|
||||
i2c_dw_disable_int(dev);
|
||||
|
||||
/* Initiate messages read/write transaction */
|
||||
for (msg_wrt_idx = 0; msg_wrt_idx < num_msgs; msg_wrt_idx++) {
|
||||
tx_buf = msgs[msg_wrt_idx].buf;
|
||||
buf_len = msgs[msg_wrt_idx].len;
|
||||
|
||||
if (!(msgs[msg_wrt_idx].flags & I2C_M_RD))
|
||||
regmap_write(dev->map, DW_IC_TX_TL, buf_len - 1);
|
||||
/*
|
||||
* Initiate the i2c read/write transaction of buffer length,
|
||||
* and poll for bus busy status. For the last message transfer,
|
||||
* update the command with stopbit enable.
|
||||
*/
|
||||
for (msg_itr_lmt = buf_len; msg_itr_lmt > 0; msg_itr_lmt--) {
|
||||
if (msg_wrt_idx == num_msgs - 1 && msg_itr_lmt == 1)
|
||||
cmd |= BIT(9);
|
||||
|
||||
if (msgs[msg_wrt_idx].flags & I2C_M_RD) {
|
||||
/* Due to hardware bug, need to write the same command twice. */
|
||||
regmap_write(dev->map, DW_IC_DATA_CMD, 0x100);
|
||||
regmap_write(dev->map, DW_IC_DATA_CMD, 0x100 | cmd);
|
||||
if (cmd) {
|
||||
regmap_write(dev->map, DW_IC_TX_TL, 2 * (buf_len - 1));
|
||||
regmap_write(dev->map, DW_IC_RX_TL, 2 * (buf_len - 1));
|
||||
/*
|
||||
* Need to check the stop bit. However, it cannot be
|
||||
* detected from the registers so we check it always
|
||||
* when read/write the last byte.
|
||||
*/
|
||||
status = i2c_dw_status(dev);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
for (data_idx = 0; data_idx < buf_len; data_idx++) {
|
||||
regmap_read(dev->map, DW_IC_DATA_CMD, &val);
|
||||
tx_buf[data_idx] = val;
|
||||
}
|
||||
status = i2c_dw_check_stopbit(dev);
|
||||
if (status)
|
||||
return status;
|
||||
}
|
||||
} else {
|
||||
regmap_write(dev->map, DW_IC_DATA_CMD, *tx_buf++ | cmd);
|
||||
usleep_range(AMD_TIMEOUT_MIN_US, AMD_TIMEOUT_MAX_US);
|
||||
}
|
||||
}
|
||||
status = i2c_dw_check_stopbit(dev);
|
||||
if (status)
|
||||
return status;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initiate (and continue) low level master read/write transaction.
|
||||
* This function is only called from i2c_dw_isr, and pumping i2c_msg
|
||||
@ -463,6 +557,16 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
||||
|
||||
pm_runtime_get_sync(dev->dev);
|
||||
|
||||
/*
|
||||
* Initiate I2C message transfer when AMD NAVI GPU card is enabled,
|
||||
* As it is polling based transfer mechanism, which does not support
|
||||
* interrupt based functionalities of existing DesignWare driver.
|
||||
*/
|
||||
if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU) {
|
||||
ret = amd_i2c_dw_xfer_quirk(adap, msgs, num);
|
||||
goto done_nolock;
|
||||
}
|
||||
|
||||
if (dev_WARN_ONCE(dev->dev, dev->suspended, "Transfer while suspended\n")) {
|
||||
ret = -ESHUTDOWN;
|
||||
goto done_nolock;
|
||||
@ -739,6 +843,20 @@ static int i2c_dw_init_recovery_info(struct dw_i2c_dev *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amd_i2c_adap_quirk(struct dw_i2c_dev *dev)
|
||||
{
|
||||
struct i2c_adapter *adap = &dev->adapter;
|
||||
int ret;
|
||||
|
||||
pm_runtime_get_noresume(dev->dev);
|
||||
ret = i2c_add_numbered_adapter(adap);
|
||||
if (ret)
|
||||
dev_err(dev->dev, "Failed to add adapter: %d\n", ret);
|
||||
pm_runtime_put_noidle(dev->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int i2c_dw_probe_master(struct dw_i2c_dev *dev)
|
||||
{
|
||||
struct i2c_adapter *adap = &dev->adapter;
|
||||
@ -775,6 +893,9 @@ int i2c_dw_probe_master(struct dw_i2c_dev *dev)
|
||||
adap->dev.parent = dev->dev;
|
||||
i2c_set_adapdata(adap, dev);
|
||||
|
||||
if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU)
|
||||
return amd_i2c_adap_quirk(dev);
|
||||
|
||||
if (dev->flags & ACCESS_NO_IRQ_SUSPEND) {
|
||||
irq_flags = IRQF_NO_SUSPEND;
|
||||
} else {
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "i2c-designware-core.h"
|
||||
|
||||
#define DRIVER_NAME "i2c-designware-pci"
|
||||
#define AMD_CLK_RATE_HZ 100000
|
||||
|
||||
enum dw_pci_ctl_id_t {
|
||||
medfield,
|
||||
@ -34,6 +35,7 @@ enum dw_pci_ctl_id_t {
|
||||
cherrytrail,
|
||||
haswell,
|
||||
elkhartlake,
|
||||
navi_amd,
|
||||
};
|
||||
|
||||
struct dw_scl_sda_cfg {
|
||||
@ -78,11 +80,23 @@ static struct dw_scl_sda_cfg hsw_config = {
|
||||
.sda_hold = 0x9,
|
||||
};
|
||||
|
||||
/* NAVI-AMD HCNT/LCNT/SDA hold time */
|
||||
static struct dw_scl_sda_cfg navi_amd_config = {
|
||||
.ss_hcnt = 0x1ae,
|
||||
.ss_lcnt = 0x23a,
|
||||
.sda_hold = 0x9,
|
||||
};
|
||||
|
||||
static u32 mfld_get_clk_rate_khz(struct dw_i2c_dev *dev)
|
||||
{
|
||||
return 25000;
|
||||
}
|
||||
|
||||
static u32 navi_amd_get_clk_rate_khz(struct dw_i2c_dev *dev)
|
||||
{
|
||||
return AMD_CLK_RATE_HZ;
|
||||
}
|
||||
|
||||
static int mfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
|
||||
{
|
||||
struct dw_i2c_dev *dev = dev_get_drvdata(&pdev->dev);
|
||||
@ -104,6 +118,35 @@ static int mfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO find a better way how to deduplicate instantiation
|
||||
* of USB PD slave device from nVidia GPU driver.
|
||||
*/
|
||||
static int navi_amd_register_client(struct dw_i2c_dev *dev)
|
||||
{
|
||||
struct i2c_board_info info;
|
||||
|
||||
memset(&info, 0, sizeof(struct i2c_board_info));
|
||||
strscpy(info.type, "ccgx-ucsi", I2C_NAME_SIZE);
|
||||
info.addr = 0x08;
|
||||
info.irq = dev->irq;
|
||||
|
||||
dev->slave = i2c_new_client_device(&dev->adapter, &info);
|
||||
if (IS_ERR(dev->slave))
|
||||
return PTR_ERR(dev->slave);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int navi_amd_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
|
||||
{
|
||||
struct dw_i2c_dev *dev = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
dev->flags |= MODEL_AMD_NAVI_GPU;
|
||||
dev->timings.bus_freq_hz = I2C_MAX_STANDARD_MODE_FREQ;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mrfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
|
||||
{
|
||||
/*
|
||||
@ -155,6 +198,12 @@ static struct dw_pci_controller dw_pci_controllers[] = {
|
||||
.bus_num = -1,
|
||||
.get_clk_rate_khz = ehl_get_clk_rate_khz,
|
||||
},
|
||||
[navi_amd] = {
|
||||
.bus_num = -1,
|
||||
.scl_sda_cfg = &navi_amd_config,
|
||||
.setup = navi_amd_setup,
|
||||
.get_clk_rate_khz = navi_amd_get_clk_rate_khz,
|
||||
},
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
@ -274,6 +323,14 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
|
||||
return r;
|
||||
}
|
||||
|
||||
if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU) {
|
||||
r = navi_amd_register_client(dev);
|
||||
if (r) {
|
||||
dev_err(dev->dev, "register client failed with %d\n", r);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, 1000);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_put_autosuspend(&pdev->dev);
|
||||
@ -337,6 +394,10 @@ static const struct pci_device_id i2_designware_pci_ids[] = {
|
||||
{ PCI_VDEVICE(INTEL, 0x4bbe), elkhartlake },
|
||||
{ PCI_VDEVICE(INTEL, 0x4bbf), elkhartlake },
|
||||
{ PCI_VDEVICE(INTEL, 0x4bc0), elkhartlake },
|
||||
{ PCI_VDEVICE(ATI, 0x7314), navi_amd },
|
||||
{ PCI_VDEVICE(ATI, 0x73a4), navi_amd },
|
||||
{ PCI_VDEVICE(ATI, 0x73e4), navi_amd },
|
||||
{ PCI_VDEVICE(ATI, 0x73c4), navi_amd },
|
||||
{ 0,}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, i2_designware_pci_ids);
|
||||
|
@ -395,7 +395,10 @@ static int em_i2c_probe(struct platform_device *pdev)
|
||||
|
||||
em_i2c_reset(&priv->adap);
|
||||
|
||||
priv->irq = platform_get_irq(pdev, 0);
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0)
|
||||
goto err_clk;
|
||||
priv->irq = ret;
|
||||
ret = devm_request_irq(&pdev->dev, priv->irq, em_i2c_irq_handler, 0,
|
||||
"em_i2c", priv);
|
||||
if (ret)
|
||||
|
504
drivers/i2c/busses/i2c-hisi.c
Normal file
504
drivers/i2c/busses/i2c-hisi.c
Normal file
@ -0,0 +1,504 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* HiSilicon I2C Controller Driver for Kunpeng SoC
|
||||
*
|
||||
* Copyright (c) 2021 HiSilicon Technologies Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
|
||||
#define HISI_I2C_FRAME_CTRL 0x0000
|
||||
#define HISI_I2C_FRAME_CTRL_SPEED_MODE GENMASK(1, 0)
|
||||
#define HISI_I2C_FRAME_CTRL_ADDR_TEN BIT(2)
|
||||
#define HISI_I2C_SLV_ADDR 0x0004
|
||||
#define HISI_I2C_SLV_ADDR_VAL GENMASK(9, 0)
|
||||
#define HISI_I2C_SLV_ADDR_GC_S_MODE BIT(10)
|
||||
#define HISI_I2C_SLV_ADDR_GC_S_EN BIT(11)
|
||||
#define HISI_I2C_CMD_TXDATA 0x0008
|
||||
#define HISI_I2C_CMD_TXDATA_DATA GENMASK(7, 0)
|
||||
#define HISI_I2C_CMD_TXDATA_RW BIT(8)
|
||||
#define HISI_I2C_CMD_TXDATA_P_EN BIT(9)
|
||||
#define HISI_I2C_CMD_TXDATA_SR_EN BIT(10)
|
||||
#define HISI_I2C_RXDATA 0x000c
|
||||
#define HISI_I2C_RXDATA_DATA GENMASK(7, 0)
|
||||
#define HISI_I2C_SS_SCL_HCNT 0x0010
|
||||
#define HISI_I2C_SS_SCL_LCNT 0x0014
|
||||
#define HISI_I2C_FS_SCL_HCNT 0x0018
|
||||
#define HISI_I2C_FS_SCL_LCNT 0x001c
|
||||
#define HISI_I2C_HS_SCL_HCNT 0x0020
|
||||
#define HISI_I2C_HS_SCL_LCNT 0x0024
|
||||
#define HISI_I2C_FIFO_CTRL 0x0028
|
||||
#define HISI_I2C_FIFO_RX_CLR BIT(0)
|
||||
#define HISI_I2C_FIFO_TX_CLR BIT(1)
|
||||
#define HISI_I2C_FIFO_RX_AF_THRESH GENMASK(7, 2)
|
||||
#define HISI_I2C_FIFO_TX_AE_THRESH GENMASK(13, 8)
|
||||
#define HISI_I2C_FIFO_STATE 0x002c
|
||||
#define HISI_I2C_FIFO_STATE_RX_RERR BIT(0)
|
||||
#define HISI_I2C_FIFO_STATE_RX_WERR BIT(1)
|
||||
#define HISI_I2C_FIFO_STATE_RX_EMPTY BIT(3)
|
||||
#define HISI_I2C_FIFO_STATE_TX_RERR BIT(6)
|
||||
#define HISI_I2C_FIFO_STATE_TX_WERR BIT(7)
|
||||
#define HISI_I2C_FIFO_STATE_TX_FULL BIT(11)
|
||||
#define HISI_I2C_SDA_HOLD 0x0030
|
||||
#define HISI_I2C_SDA_HOLD_TX GENMASK(15, 0)
|
||||
#define HISI_I2C_SDA_HOLD_RX GENMASK(23, 16)
|
||||
#define HISI_I2C_FS_SPK_LEN 0x0038
|
||||
#define HISI_I2C_FS_SPK_LEN_CNT GENMASK(7, 0)
|
||||
#define HISI_I2C_HS_SPK_LEN 0x003c
|
||||
#define HISI_I2C_HS_SPK_LEN_CNT GENMASK(7, 0)
|
||||
#define HISI_I2C_INT_MSTAT 0x0044
|
||||
#define HISI_I2C_INT_CLR 0x0048
|
||||
#define HISI_I2C_INT_MASK 0x004C
|
||||
#define HISI_I2C_TRANS_STATE 0x0050
|
||||
#define HISI_I2C_TRANS_ERR 0x0054
|
||||
#define HISI_I2C_VERSION 0x0058
|
||||
|
||||
#define HISI_I2C_INT_ALL GENMASK(4, 0)
|
||||
#define HISI_I2C_INT_TRANS_CPLT BIT(0)
|
||||
#define HISI_I2C_INT_TRANS_ERR BIT(1)
|
||||
#define HISI_I2C_INT_FIFO_ERR BIT(2)
|
||||
#define HISI_I2C_INT_RX_FULL BIT(3)
|
||||
#define HISI_I2C_INT_TX_EMPTY BIT(4)
|
||||
#define HISI_I2C_INT_ERR \
|
||||
(HISI_I2C_INT_TRANS_ERR | HISI_I2C_INT_FIFO_ERR)
|
||||
|
||||
#define HISI_I2C_STD_SPEED_MODE 0
|
||||
#define HISI_I2C_FAST_SPEED_MODE 1
|
||||
#define HISI_I2C_HIGH_SPEED_MODE 2
|
||||
|
||||
#define HISI_I2C_TX_FIFO_DEPTH 64
|
||||
#define HISI_I2C_RX_FIFO_DEPTH 64
|
||||
#define HISI_I2C_TX_F_AE_THRESH 1
|
||||
#define HISI_I2C_RX_F_AF_THRESH 60
|
||||
|
||||
#define HZ_PER_KHZ 1000
|
||||
|
||||
#define NSEC_TO_CYCLES(ns, clk_rate_khz) \
|
||||
DIV_ROUND_UP_ULL((clk_rate_khz) * (ns), NSEC_PER_MSEC)
|
||||
|
||||
struct hisi_i2c_controller {
|
||||
struct i2c_adapter adapter;
|
||||
void __iomem *iobase;
|
||||
struct device *dev;
|
||||
int irq;
|
||||
|
||||
/* Intermediates for recording the transfer process */
|
||||
struct completion *completion;
|
||||
struct i2c_msg *msgs;
|
||||
int msg_num;
|
||||
int msg_tx_idx;
|
||||
int buf_tx_idx;
|
||||
int msg_rx_idx;
|
||||
int buf_rx_idx;
|
||||
u16 tar_addr;
|
||||
u32 xfer_err;
|
||||
|
||||
/* I2C bus configuration */
|
||||
struct i2c_timings t;
|
||||
u32 clk_rate_khz;
|
||||
u32 spk_len;
|
||||
};
|
||||
|
||||
static void hisi_i2c_enable_int(struct hisi_i2c_controller *ctlr, u32 mask)
|
||||
{
|
||||
writel_relaxed(mask, ctlr->iobase + HISI_I2C_INT_MASK);
|
||||
}
|
||||
|
||||
static void hisi_i2c_disable_int(struct hisi_i2c_controller *ctlr, u32 mask)
|
||||
{
|
||||
writel_relaxed((~mask) & HISI_I2C_INT_ALL, ctlr->iobase + HISI_I2C_INT_MASK);
|
||||
}
|
||||
|
||||
static void hisi_i2c_clear_int(struct hisi_i2c_controller *ctlr, u32 mask)
|
||||
{
|
||||
writel_relaxed(mask, ctlr->iobase + HISI_I2C_INT_CLR);
|
||||
}
|
||||
|
||||
static void hisi_i2c_handle_errors(struct hisi_i2c_controller *ctlr)
|
||||
{
|
||||
u32 int_err = ctlr->xfer_err, reg;
|
||||
|
||||
if (int_err & HISI_I2C_INT_FIFO_ERR) {
|
||||
reg = readl(ctlr->iobase + HISI_I2C_FIFO_STATE);
|
||||
|
||||
if (reg & HISI_I2C_FIFO_STATE_RX_RERR)
|
||||
dev_err(ctlr->dev, "rx fifo error read\n");
|
||||
|
||||
if (reg & HISI_I2C_FIFO_STATE_RX_WERR)
|
||||
dev_err(ctlr->dev, "rx fifo error write\n");
|
||||
|
||||
if (reg & HISI_I2C_FIFO_STATE_TX_RERR)
|
||||
dev_err(ctlr->dev, "tx fifo error read\n");
|
||||
|
||||
if (reg & HISI_I2C_FIFO_STATE_TX_WERR)
|
||||
dev_err(ctlr->dev, "tx fifo error write\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int hisi_i2c_start_xfer(struct hisi_i2c_controller *ctlr)
|
||||
{
|
||||
struct i2c_msg *msg = ctlr->msgs;
|
||||
u32 reg;
|
||||
|
||||
reg = readl(ctlr->iobase + HISI_I2C_FRAME_CTRL);
|
||||
reg &= ~HISI_I2C_FRAME_CTRL_ADDR_TEN;
|
||||
if (msg->flags & I2C_M_TEN)
|
||||
reg |= HISI_I2C_FRAME_CTRL_ADDR_TEN;
|
||||
writel(reg, ctlr->iobase + HISI_I2C_FRAME_CTRL);
|
||||
|
||||
reg = readl(ctlr->iobase + HISI_I2C_SLV_ADDR);
|
||||
reg &= ~HISI_I2C_SLV_ADDR_VAL;
|
||||
reg |= FIELD_PREP(HISI_I2C_SLV_ADDR_VAL, msg->addr);
|
||||
writel(reg, ctlr->iobase + HISI_I2C_SLV_ADDR);
|
||||
|
||||
reg = readl(ctlr->iobase + HISI_I2C_FIFO_CTRL);
|
||||
reg |= HISI_I2C_FIFO_RX_CLR | HISI_I2C_FIFO_TX_CLR;
|
||||
writel(reg, ctlr->iobase + HISI_I2C_FIFO_CTRL);
|
||||
reg &= ~(HISI_I2C_FIFO_RX_CLR | HISI_I2C_FIFO_TX_CLR);
|
||||
writel(reg, ctlr->iobase + HISI_I2C_FIFO_CTRL);
|
||||
|
||||
hisi_i2c_clear_int(ctlr, HISI_I2C_INT_ALL);
|
||||
hisi_i2c_enable_int(ctlr, HISI_I2C_INT_ALL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hisi_i2c_reset_xfer(struct hisi_i2c_controller *ctlr)
|
||||
{
|
||||
ctlr->msg_num = 0;
|
||||
ctlr->xfer_err = 0;
|
||||
ctlr->msg_tx_idx = 0;
|
||||
ctlr->msg_rx_idx = 0;
|
||||
ctlr->buf_tx_idx = 0;
|
||||
ctlr->buf_rx_idx = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the transfer information and start the I2C bus transfer.
|
||||
* We only configure the transfer and do some pre/post works here, and
|
||||
* wait for the transfer done. The major transfer process is performed
|
||||
* in the IRQ handler.
|
||||
*/
|
||||
static int hisi_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||
int num)
|
||||
{
|
||||
struct hisi_i2c_controller *ctlr = i2c_get_adapdata(adap);
|
||||
DECLARE_COMPLETION_ONSTACK(done);
|
||||
int ret = num;
|
||||
|
||||
hisi_i2c_reset_xfer(ctlr);
|
||||
ctlr->completion = &done;
|
||||
ctlr->msg_num = num;
|
||||
ctlr->msgs = msgs;
|
||||
|
||||
hisi_i2c_start_xfer(ctlr);
|
||||
|
||||
if (!wait_for_completion_timeout(ctlr->completion, adap->timeout)) {
|
||||
hisi_i2c_disable_int(ctlr, HISI_I2C_INT_ALL);
|
||||
synchronize_irq(ctlr->irq);
|
||||
i2c_recover_bus(&ctlr->adapter);
|
||||
dev_err(ctlr->dev, "bus transfer timeout\n");
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
if (ctlr->xfer_err) {
|
||||
hisi_i2c_handle_errors(ctlr);
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
hisi_i2c_reset_xfer(ctlr);
|
||||
ctlr->completion = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u32 hisi_i2c_functionality(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm hisi_i2c_algo = {
|
||||
.master_xfer = hisi_i2c_master_xfer,
|
||||
.functionality = hisi_i2c_functionality,
|
||||
};
|
||||
|
||||
static int hisi_i2c_read_rx_fifo(struct hisi_i2c_controller *ctlr)
|
||||
{
|
||||
struct i2c_msg *cur_msg;
|
||||
u32 fifo_state;
|
||||
|
||||
while (ctlr->msg_rx_idx < ctlr->msg_num) {
|
||||
cur_msg = ctlr->msgs + ctlr->msg_rx_idx;
|
||||
|
||||
if (!(cur_msg->flags & I2C_M_RD)) {
|
||||
ctlr->msg_rx_idx++;
|
||||
continue;
|
||||
}
|
||||
|
||||
fifo_state = readl(ctlr->iobase + HISI_I2C_FIFO_STATE);
|
||||
while (!(fifo_state & HISI_I2C_FIFO_STATE_RX_EMPTY) &&
|
||||
ctlr->buf_rx_idx < cur_msg->len) {
|
||||
cur_msg->buf[ctlr->buf_rx_idx++] = readl(ctlr->iobase + HISI_I2C_RXDATA);
|
||||
fifo_state = readl(ctlr->iobase + HISI_I2C_FIFO_STATE);
|
||||
}
|
||||
|
||||
if (ctlr->buf_rx_idx == cur_msg->len) {
|
||||
ctlr->buf_rx_idx = 0;
|
||||
ctlr->msg_rx_idx++;
|
||||
}
|
||||
|
||||
if (fifo_state & HISI_I2C_FIFO_STATE_RX_EMPTY)
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hisi_i2c_xfer_msg(struct hisi_i2c_controller *ctlr)
|
||||
{
|
||||
int max_write = HISI_I2C_TX_FIFO_DEPTH;
|
||||
bool need_restart = false, last_msg;
|
||||
struct i2c_msg *cur_msg;
|
||||
u32 cmd, fifo_state;
|
||||
|
||||
while (ctlr->msg_tx_idx < ctlr->msg_num) {
|
||||
cur_msg = ctlr->msgs + ctlr->msg_tx_idx;
|
||||
last_msg = (ctlr->msg_tx_idx == ctlr->msg_num - 1);
|
||||
|
||||
/* Signal the SR bit when we start transferring a new message */
|
||||
if (ctlr->msg_tx_idx && !ctlr->buf_tx_idx)
|
||||
need_restart = true;
|
||||
|
||||
fifo_state = readl(ctlr->iobase + HISI_I2C_FIFO_STATE);
|
||||
while (!(fifo_state & HISI_I2C_FIFO_STATE_TX_FULL) &&
|
||||
ctlr->buf_tx_idx < cur_msg->len && max_write) {
|
||||
cmd = 0;
|
||||
|
||||
if (need_restart) {
|
||||
cmd |= HISI_I2C_CMD_TXDATA_SR_EN;
|
||||
need_restart = false;
|
||||
}
|
||||
|
||||
/* Signal the STOP bit at the last frame of the last message */
|
||||
if (ctlr->buf_tx_idx == cur_msg->len - 1 && last_msg)
|
||||
cmd |= HISI_I2C_CMD_TXDATA_P_EN;
|
||||
|
||||
if (cur_msg->flags & I2C_M_RD)
|
||||
cmd |= HISI_I2C_CMD_TXDATA_RW;
|
||||
else
|
||||
cmd |= FIELD_PREP(HISI_I2C_CMD_TXDATA_DATA,
|
||||
cur_msg->buf[ctlr->buf_tx_idx]);
|
||||
|
||||
writel(cmd, ctlr->iobase + HISI_I2C_CMD_TXDATA);
|
||||
ctlr->buf_tx_idx++;
|
||||
max_write--;
|
||||
|
||||
fifo_state = readl(ctlr->iobase + HISI_I2C_FIFO_STATE);
|
||||
}
|
||||
|
||||
/* Update the transfer index after per message transfer is done. */
|
||||
if (ctlr->buf_tx_idx == cur_msg->len) {
|
||||
ctlr->buf_tx_idx = 0;
|
||||
ctlr->msg_tx_idx++;
|
||||
}
|
||||
|
||||
if ((fifo_state & HISI_I2C_FIFO_STATE_TX_FULL) ||
|
||||
max_write == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t hisi_i2c_irq(int irq, void *context)
|
||||
{
|
||||
struct hisi_i2c_controller *ctlr = context;
|
||||
u32 int_stat;
|
||||
|
||||
int_stat = readl(ctlr->iobase + HISI_I2C_INT_MSTAT);
|
||||
hisi_i2c_clear_int(ctlr, int_stat);
|
||||
if (!(int_stat & HISI_I2C_INT_ALL))
|
||||
return IRQ_NONE;
|
||||
|
||||
if (int_stat & HISI_I2C_INT_TX_EMPTY)
|
||||
hisi_i2c_xfer_msg(ctlr);
|
||||
|
||||
if (int_stat & HISI_I2C_INT_ERR) {
|
||||
ctlr->xfer_err = int_stat;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Drain the rx fifo before finish the transfer */
|
||||
if (int_stat & (HISI_I2C_INT_TRANS_CPLT | HISI_I2C_INT_RX_FULL))
|
||||
hisi_i2c_read_rx_fifo(ctlr);
|
||||
|
||||
out:
|
||||
if (int_stat & HISI_I2C_INT_TRANS_CPLT || ctlr->xfer_err) {
|
||||
hisi_i2c_disable_int(ctlr, HISI_I2C_INT_ALL);
|
||||
hisi_i2c_clear_int(ctlr, HISI_I2C_INT_ALL);
|
||||
complete(ctlr->completion);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function for calculating and configuring the HIGH and LOW
|
||||
* periods of SCL clock. The caller will pass the ratio of the
|
||||
* counts (divide / divisor) according to the target speed mode,
|
||||
* and the target registers.
|
||||
*/
|
||||
static void hisi_i2c_set_scl(struct hisi_i2c_controller *ctlr,
|
||||
u32 divide, u32 divisor,
|
||||
u32 reg_hcnt, u32 reg_lcnt)
|
||||
{
|
||||
u32 total_cnt, t_scl_hcnt, t_scl_lcnt, scl_fall_cnt, scl_rise_cnt;
|
||||
u32 scl_hcnt, scl_lcnt;
|
||||
|
||||
/* Total SCL clock cycles per speed period */
|
||||
total_cnt = DIV_ROUND_UP_ULL(ctlr->clk_rate_khz * HZ_PER_KHZ, ctlr->t.bus_freq_hz);
|
||||
/* Total HIGH level SCL clock cycles including edges */
|
||||
t_scl_hcnt = DIV_ROUND_UP_ULL(total_cnt * divide, divisor);
|
||||
/* Total LOW level SCL clock cycles including edges */
|
||||
t_scl_lcnt = total_cnt - t_scl_hcnt;
|
||||
/* Fall edge SCL clock cycles */
|
||||
scl_fall_cnt = NSEC_TO_CYCLES(ctlr->t.scl_fall_ns, ctlr->clk_rate_khz);
|
||||
/* Rise edge SCL clock cycles */
|
||||
scl_rise_cnt = NSEC_TO_CYCLES(ctlr->t.scl_rise_ns, ctlr->clk_rate_khz);
|
||||
|
||||
/* Calculated HIGH and LOW periods of SCL clock */
|
||||
scl_hcnt = t_scl_hcnt - ctlr->spk_len - 7 - scl_fall_cnt;
|
||||
scl_lcnt = t_scl_lcnt - 1 - scl_rise_cnt;
|
||||
|
||||
writel(scl_hcnt, ctlr->iobase + reg_hcnt);
|
||||
writel(scl_lcnt, ctlr->iobase + reg_lcnt);
|
||||
}
|
||||
|
||||
static void hisi_i2c_configure_bus(struct hisi_i2c_controller *ctlr)
|
||||
{
|
||||
u32 reg, sda_hold_cnt, speed_mode;
|
||||
|
||||
i2c_parse_fw_timings(ctlr->dev, &ctlr->t, true);
|
||||
ctlr->spk_len = NSEC_TO_CYCLES(ctlr->t.digital_filter_width_ns, ctlr->clk_rate_khz);
|
||||
|
||||
switch (ctlr->t.bus_freq_hz) {
|
||||
case I2C_MAX_FAST_MODE_FREQ:
|
||||
speed_mode = HISI_I2C_FAST_SPEED_MODE;
|
||||
hisi_i2c_set_scl(ctlr, 26, 76, HISI_I2C_FS_SCL_HCNT, HISI_I2C_FS_SCL_LCNT);
|
||||
break;
|
||||
case I2C_MAX_HIGH_SPEED_MODE_FREQ:
|
||||
speed_mode = HISI_I2C_HIGH_SPEED_MODE;
|
||||
hisi_i2c_set_scl(ctlr, 6, 22, HISI_I2C_HS_SCL_HCNT, HISI_I2C_HS_SCL_LCNT);
|
||||
break;
|
||||
case I2C_MAX_STANDARD_MODE_FREQ:
|
||||
default:
|
||||
speed_mode = HISI_I2C_STD_SPEED_MODE;
|
||||
|
||||
/* For default condition force the bus speed to standard mode. */
|
||||
ctlr->t.bus_freq_hz = I2C_MAX_STANDARD_MODE_FREQ;
|
||||
hisi_i2c_set_scl(ctlr, 40, 87, HISI_I2C_SS_SCL_HCNT, HISI_I2C_SS_SCL_LCNT);
|
||||
break;
|
||||
}
|
||||
|
||||
reg = readl(ctlr->iobase + HISI_I2C_FRAME_CTRL);
|
||||
reg &= ~HISI_I2C_FRAME_CTRL_SPEED_MODE;
|
||||
reg |= FIELD_PREP(HISI_I2C_FRAME_CTRL_SPEED_MODE, speed_mode);
|
||||
writel(reg, ctlr->iobase + HISI_I2C_FRAME_CTRL);
|
||||
|
||||
sda_hold_cnt = NSEC_TO_CYCLES(ctlr->t.sda_hold_ns, ctlr->clk_rate_khz);
|
||||
|
||||
reg = FIELD_PREP(HISI_I2C_SDA_HOLD_TX, sda_hold_cnt);
|
||||
writel(reg, ctlr->iobase + HISI_I2C_SDA_HOLD);
|
||||
|
||||
writel(ctlr->spk_len, ctlr->iobase + HISI_I2C_FS_SPK_LEN);
|
||||
|
||||
reg = FIELD_PREP(HISI_I2C_FIFO_RX_AF_THRESH, HISI_I2C_RX_F_AF_THRESH);
|
||||
reg |= FIELD_PREP(HISI_I2C_FIFO_TX_AE_THRESH, HISI_I2C_TX_F_AE_THRESH);
|
||||
writel(reg, ctlr->iobase + HISI_I2C_FIFO_CTRL);
|
||||
}
|
||||
|
||||
static int hisi_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct hisi_i2c_controller *ctlr;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct i2c_adapter *adapter;
|
||||
u64 clk_rate_hz;
|
||||
u32 hw_version;
|
||||
int ret;
|
||||
|
||||
ctlr = devm_kzalloc(dev, sizeof(*ctlr), GFP_KERNEL);
|
||||
if (!ctlr)
|
||||
return -ENOMEM;
|
||||
|
||||
ctlr->iobase = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(ctlr->iobase))
|
||||
return PTR_ERR(ctlr->iobase);
|
||||
|
||||
ctlr->irq = platform_get_irq(pdev, 0);
|
||||
if (ctlr->irq < 0)
|
||||
return ctlr->irq;
|
||||
|
||||
ctlr->dev = dev;
|
||||
|
||||
hisi_i2c_disable_int(ctlr, HISI_I2C_INT_ALL);
|
||||
|
||||
ret = devm_request_irq(dev, ctlr->irq, hisi_i2c_irq, 0, "hisi-i2c", ctlr);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request irq handler, ret = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = device_property_read_u64(dev, "clk_rate", &clk_rate_hz);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to get clock frequency, ret = %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ctlr->clk_rate_khz = DIV_ROUND_UP_ULL(clk_rate_hz, HZ_PER_KHZ);
|
||||
|
||||
hisi_i2c_configure_bus(ctlr);
|
||||
|
||||
adapter = &ctlr->adapter;
|
||||
snprintf(adapter->name, sizeof(adapter->name),
|
||||
"HiSilicon I2C Controller %s", dev_name(dev));
|
||||
adapter->owner = THIS_MODULE;
|
||||
adapter->algo = &hisi_i2c_algo;
|
||||
adapter->dev.parent = dev;
|
||||
i2c_set_adapdata(adapter, ctlr);
|
||||
|
||||
ret = devm_i2c_add_adapter(dev, adapter);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
hw_version = readl(ctlr->iobase + HISI_I2C_VERSION);
|
||||
dev_info(ctlr->dev, "speed mode is %s. hw version 0x%x\n",
|
||||
i2c_freq_mode_string(ctlr->t.bus_freq_hz), hw_version);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id hisi_i2c_acpi_ids[] = {
|
||||
{ "HISI03D1", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, hisi_i2c_acpi_ids);
|
||||
|
||||
static struct platform_driver hisi_i2c_driver = {
|
||||
.probe = hisi_i2c_probe,
|
||||
.driver = {
|
||||
.name = "hisi-i2c",
|
||||
.acpi_match_table = hisi_i2c_acpi_ids,
|
||||
},
|
||||
};
|
||||
module_platform_driver(hisi_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Yicong Yang <yangyicong@hisilicon.com>");
|
||||
MODULE_DESCRIPTION("HiSilicon I2C Controller Driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -73,6 +73,7 @@
|
||||
* Comet Lake-V (PCH) 0xa3a3 32 hard yes yes yes
|
||||
* Alder Lake-S (PCH) 0x7aa3 32 hard yes yes yes
|
||||
* Alder Lake-P (PCH) 0x51a3 32 hard yes yes yes
|
||||
* Alder Lake-M (PCH) 0x54a3 32 hard yes yes yes
|
||||
*
|
||||
* Features supported by this driver:
|
||||
* Software PEC no
|
||||
@ -230,6 +231,7 @@
|
||||
#define PCI_DEVICE_ID_INTEL_ELKHART_LAKE_SMBUS 0x4b23
|
||||
#define PCI_DEVICE_ID_INTEL_JASPER_LAKE_SMBUS 0x4da3
|
||||
#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_P_SMBUS 0x51a3
|
||||
#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_M_SMBUS 0x54a3
|
||||
#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
|
||||
@ -1087,6 +1089,7 @@ static const struct pci_device_id i801_ids[] = {
|
||||
{ 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) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ALDER_LAKE_P_SMBUS) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ALDER_LAKE_M_SMBUS) },
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
@ -1771,6 +1774,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
case PCI_DEVICE_ID_INTEL_EBG_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_ALDER_LAKE_S_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_ALDER_LAKE_P_SMBUS:
|
||||
case PCI_DEVICE_ID_INTEL_ALDER_LAKE_M_SMBUS:
|
||||
priv->features |= FEATURE_BLOCK_PROC;
|
||||
priv->features |= FEATURE_I2C_BLOCK_READ;
|
||||
priv->features |= FEATURE_IRQ;
|
||||
|
@ -54,7 +54,6 @@ struct icy_i2c {
|
||||
|
||||
void __iomem *reg_s0;
|
||||
void __iomem *reg_s1;
|
||||
struct fwnode_handle *ltc2990_fwnode;
|
||||
struct i2c_client *ltc2990_client;
|
||||
};
|
||||
|
||||
@ -115,6 +114,10 @@ static const struct property_entry icy_ltc2990_props[] = {
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node icy_ltc2990_node = {
|
||||
.properties = icy_ltc2990_props,
|
||||
};
|
||||
|
||||
static int icy_probe(struct zorro_dev *z,
|
||||
const struct zorro_device_id *ent)
|
||||
{
|
||||
@ -123,6 +126,7 @@ static int icy_probe(struct zorro_dev *z,
|
||||
struct fwnode_handle *new_fwnode;
|
||||
struct i2c_board_info ltc2990_info = {
|
||||
.type = "ltc2990",
|
||||
.swnode = &icy_ltc2990_node,
|
||||
};
|
||||
|
||||
i2c = devm_kzalloc(&z->dev, sizeof(*i2c), GFP_KERNEL);
|
||||
@ -174,26 +178,10 @@ static int icy_probe(struct zorro_dev *z,
|
||||
*
|
||||
* See property_entry above for in1, in2, temp3.
|
||||
*/
|
||||
new_fwnode = fwnode_create_software_node(icy_ltc2990_props, NULL);
|
||||
if (IS_ERR(new_fwnode)) {
|
||||
dev_info(&z->dev, "Failed to create fwnode for LTC2990, error: %ld\n",
|
||||
PTR_ERR(new_fwnode));
|
||||
} else {
|
||||
/*
|
||||
* Store the fwnode so we can destroy it on .remove().
|
||||
* Only store it on success, as fwnode_remove_software_node()
|
||||
* is NULL safe, but not PTR_ERR safe.
|
||||
*/
|
||||
i2c->ltc2990_fwnode = new_fwnode;
|
||||
ltc2990_info.fwnode = new_fwnode;
|
||||
|
||||
i2c->ltc2990_client =
|
||||
i2c_new_scanned_device(&i2c->adapter,
|
||||
<c2990_info,
|
||||
icy_ltc2990_addresses,
|
||||
NULL);
|
||||
}
|
||||
|
||||
i2c->ltc2990_client = i2c_new_scanned_device(&i2c->adapter,
|
||||
<c2990_info,
|
||||
icy_ltc2990_addresses,
|
||||
NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -202,8 +190,6 @@ static void icy_remove(struct zorro_dev *z)
|
||||
struct icy_i2c *i2c = dev_get_drvdata(&z->dev);
|
||||
|
||||
i2c_unregister_device(i2c->ltc2990_client);
|
||||
fwnode_remove_software_node(i2c->ltc2990_fwnode);
|
||||
|
||||
i2c_del_adapter(&i2c->adapter);
|
||||
}
|
||||
|
||||
|
@ -1057,7 +1057,7 @@ static int img_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||
atomic = true;
|
||||
}
|
||||
|
||||
ret = pm_runtime_get_sync(adap->dev.parent);
|
||||
ret = pm_runtime_resume_and_get(adap->dev.parent);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -1158,7 +1158,7 @@ static int img_i2c_init(struct img_i2c *i2c)
|
||||
u32 rev;
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_get_sync(i2c->adap.dev.parent);
|
||||
ret = pm_runtime_resume_and_get(i2c->adap.dev.parent);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -259,7 +259,7 @@ static int lpi2c_imx_master_enable(struct lpi2c_imx_struct *lpi2c_imx)
|
||||
unsigned int temp;
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_get_sync(lpi2c_imx->adapter.dev.parent);
|
||||
ret = pm_runtime_resume_and_get(lpi2c_imx->adapter.dev.parent);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -801,7 +801,7 @@ static int i2c_imx_reg_slave(struct i2c_client *client)
|
||||
i2c_imx->last_slave_event = I2C_SLAVE_STOP;
|
||||
|
||||
/* Resume */
|
||||
ret = pm_runtime_get_sync(i2c_imx->adapter.dev.parent);
|
||||
ret = pm_runtime_resume_and_get(i2c_imx->adapter.dev.parent);
|
||||
if (ret < 0) {
|
||||
dev_err(&i2c_imx->adapter.dev, "failed to resume i2c controller");
|
||||
return ret;
|
||||
@ -1253,7 +1253,7 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter,
|
||||
struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter);
|
||||
int result;
|
||||
|
||||
result = pm_runtime_get_sync(i2c_imx->adapter.dev.parent);
|
||||
result = pm_runtime_resume_and_get(i2c_imx->adapter.dev.parent);
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
@ -1496,7 +1496,7 @@ static int i2c_imx_remove(struct platform_device *pdev)
|
||||
struct imx_i2c_struct *i2c_imx = platform_get_drvdata(pdev);
|
||||
int irq, ret;
|
||||
|
||||
ret = pm_runtime_get_sync(&pdev->dev);
|
||||
ret = pm_runtime_resume_and_get(&pdev->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -125,10 +125,12 @@ iop3xx_i2c_error(u32 sr)
|
||||
int rc = 0;
|
||||
|
||||
if ((sr & IOP3XX_ISR_BERRD)) {
|
||||
if ( !rc ) rc = -I2C_ERR_BERR;
|
||||
if (!rc)
|
||||
rc = -I2C_ERR_BERR;
|
||||
}
|
||||
if ((sr & IOP3XX_ISR_ALD)) {
|
||||
if ( !rc ) rc = -I2C_ERR_ALD;
|
||||
if (!rc)
|
||||
rc = -I2C_ERR_ALD;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@ -151,12 +153,12 @@ iop3xx_i2c_get_srstat(struct i2c_algo_iop3xx_data *iop3xx_adap)
|
||||
* sleep until interrupted, then recover and analyse the SR
|
||||
* saved by handler
|
||||
*/
|
||||
typedef int (* compare_func)(unsigned test, unsigned mask);
|
||||
typedef int (*compare_func)(unsigned test, unsigned mask);
|
||||
/* returns 1 on correct comparison */
|
||||
|
||||
static int
|
||||
iop3xx_i2c_wait_event(struct i2c_algo_iop3xx_data *iop3xx_adap,
|
||||
unsigned flags, unsigned* status,
|
||||
unsigned flags, unsigned *status,
|
||||
compare_func compare)
|
||||
{
|
||||
unsigned sr = 0;
|
||||
@ -167,7 +169,7 @@ iop3xx_i2c_wait_event(struct i2c_algo_iop3xx_data *iop3xx_adap,
|
||||
do {
|
||||
interrupted = wait_event_interruptible_timeout (
|
||||
iop3xx_adap->waitq,
|
||||
(done = compare( sr = iop3xx_i2c_get_srstat(iop3xx_adap) ,flags )),
|
||||
(done = compare(sr = iop3xx_i2c_get_srstat(iop3xx_adap), flags)),
|
||||
1 * HZ
|
||||
);
|
||||
if ((rc = iop3xx_i2c_error(sr)) < 0) {
|
||||
@ -177,7 +179,7 @@ iop3xx_i2c_wait_event(struct i2c_algo_iop3xx_data *iop3xx_adap,
|
||||
*status = sr;
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
} while(!done);
|
||||
} while (!done);
|
||||
|
||||
*status = sr;
|
||||
|
||||
@ -204,7 +206,7 @@ iop3xx_i2c_wait_tx_done(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status)
|
||||
{
|
||||
return iop3xx_i2c_wait_event(
|
||||
iop3xx_adap,
|
||||
IOP3XX_ISR_TXEMPTY | IOP3XX_ISR_ALD | IOP3XX_ISR_BERRD,
|
||||
IOP3XX_ISR_TXEMPTY | IOP3XX_ISR_ALD | IOP3XX_ISR_BERRD,
|
||||
status, any_bits_set);
|
||||
}
|
||||
|
||||
@ -226,7 +228,7 @@ iop3xx_i2c_wait_idle(struct i2c_algo_iop3xx_data *iop3xx_adap, int *status)
|
||||
|
||||
static int
|
||||
iop3xx_i2c_send_target_addr(struct i2c_algo_iop3xx_data *iop3xx_adap,
|
||||
struct i2c_msg* msg)
|
||||
struct i2c_msg *msg)
|
||||
{
|
||||
unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET);
|
||||
int status;
|
||||
@ -273,7 +275,7 @@ iop3xx_i2c_write_byte(struct i2c_algo_iop3xx_data *iop3xx_adap, char byte,
|
||||
}
|
||||
|
||||
static int
|
||||
iop3xx_i2c_read_byte(struct i2c_algo_iop3xx_data *iop3xx_adap, char* byte,
|
||||
iop3xx_i2c_read_byte(struct i2c_algo_iop3xx_data *iop3xx_adap, char *byte,
|
||||
int stop)
|
||||
{
|
||||
unsigned long cr = __raw_readl(iop3xx_adap->ioaddr + CR_OFFSET);
|
||||
@ -305,7 +307,7 @@ iop3xx_i2c_writebytes(struct i2c_adapter *i2c_adap, const char *buf, int count)
|
||||
int rc = 0;
|
||||
|
||||
for (ii = 0; rc == 0 && ii != count; ++ii)
|
||||
rc = iop3xx_i2c_write_byte(iop3xx_adap, buf[ii], ii==count-1);
|
||||
rc = iop3xx_i2c_write_byte(iop3xx_adap, buf[ii], ii == count-1);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -317,7 +319,7 @@ iop3xx_i2c_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count)
|
||||
int rc = 0;
|
||||
|
||||
for (ii = 0; rc == 0 && ii != count; ++ii)
|
||||
rc = iop3xx_i2c_read_byte(iop3xx_adap, &buf[ii], ii==count-1);
|
||||
rc = iop3xx_i2c_read_byte(iop3xx_adap, &buf[ii], ii == count-1);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -330,7 +332,7 @@ iop3xx_i2c_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count)
|
||||
* condition.
|
||||
*/
|
||||
static int
|
||||
iop3xx_i2c_handle_msg(struct i2c_adapter *i2c_adap, struct i2c_msg* pmsg)
|
||||
iop3xx_i2c_handle_msg(struct i2c_adapter *i2c_adap, struct i2c_msg *pmsg)
|
||||
{
|
||||
struct i2c_algo_iop3xx_data *iop3xx_adap = i2c_adap->algo_data;
|
||||
int rc;
|
||||
@ -369,7 +371,7 @@ iop3xx_i2c_master_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
|
||||
|
||||
iop3xx_i2c_transaction_cleanup(iop3xx_adap);
|
||||
|
||||
if(ret)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return im;
|
||||
|
@ -825,7 +825,10 @@ static int jz4780_i2c_probe(struct platform_device *pdev)
|
||||
|
||||
jz4780_i2c_writew(i2c, JZ4780_I2C_INTM, 0x0);
|
||||
|
||||
i2c->irq = platform_get_irq(pdev, 0);
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
i2c->irq = ret;
|
||||
ret = devm_request_irq(&pdev->dev, i2c->irq, jz4780_i2c_irq, 0,
|
||||
dev_name(&pdev->dev), i2c);
|
||||
if (ret)
|
||||
|
@ -172,12 +172,6 @@
|
||||
#define MLXBF_I2C_SMBUS_THIGH_MAX_TBUF 0x14
|
||||
#define MLXBF_I2C_SMBUS_SCL_LOW_TIMEOUT 0x18
|
||||
|
||||
enum {
|
||||
MLXBF_I2C_TIMING_100KHZ = 100000,
|
||||
MLXBF_I2C_TIMING_400KHZ = 400000,
|
||||
MLXBF_I2C_TIMING_1000KHZ = 1000000,
|
||||
};
|
||||
|
||||
/*
|
||||
* Defines SMBus operating frequency and core clock frequency.
|
||||
* According to ADB files, default values are compliant to 100KHz SMBus
|
||||
@ -1202,7 +1196,7 @@ static int mlxbf_i2c_init_timings(struct platform_device *pdev,
|
||||
|
||||
ret = device_property_read_u32(dev, "clock-frequency", &config_khz);
|
||||
if (ret < 0)
|
||||
config_khz = MLXBF_I2C_TIMING_100KHZ;
|
||||
config_khz = I2C_MAX_STANDARD_MODE_FREQ;
|
||||
|
||||
switch (config_khz) {
|
||||
default:
|
||||
@ -1210,15 +1204,15 @@ static int mlxbf_i2c_init_timings(struct platform_device *pdev,
|
||||
pr_warn("Illegal value %d: defaulting to 100 KHz\n",
|
||||
config_khz);
|
||||
fallthrough;
|
||||
case MLXBF_I2C_TIMING_100KHZ:
|
||||
case I2C_MAX_STANDARD_MODE_FREQ:
|
||||
config_idx = MLXBF_I2C_TIMING_CONFIG_100KHZ;
|
||||
break;
|
||||
|
||||
case MLXBF_I2C_TIMING_400KHZ:
|
||||
case I2C_MAX_FAST_MODE_FREQ:
|
||||
config_idx = MLXBF_I2C_TIMING_CONFIG_400KHZ;
|
||||
break;
|
||||
|
||||
case MLXBF_I2C_TIMING_1000KHZ:
|
||||
case I2C_MAX_FAST_MODE_PLUS_FREQ:
|
||||
config_idx = MLXBF_I2C_TIMING_CONFIG_1000KHZ;
|
||||
break;
|
||||
}
|
||||
@ -2376,6 +2370,8 @@ static int mlxbf_i2c_probe(struct platform_device *pdev)
|
||||
mlxbf_i2c_init_slave(pdev, priv);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
ret = devm_request_irq(dev, irq, mlxbf_smbus_irq,
|
||||
IRQF_ONESHOT | IRQF_SHARED | IRQF_PROBE_SHARED,
|
||||
dev_name(dev), priv);
|
||||
|
@ -1,16 +1,11 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* (C) Copyright 2003-2004
|
||||
* Humboldt Solutions Ltd, adrian@humboldt.co.uk.
|
||||
|
||||
* This is a combined i2c adapter and algorithm driver for the
|
||||
* MPC107/Tsi107 PowerPC northbridge and processors that include
|
||||
* the same I2C unit (8240, 8245, 85xx).
|
||||
*
|
||||
* Release 0.8
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
* Copyright (C) 2003-2004 Humboldt Solutions Ltd, adrian@humboldt.co.uk
|
||||
* Copyright (C) 2021 Allied Telesis Labs
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
@ -19,6 +14,7 @@
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/clk.h>
|
||||
@ -58,18 +54,50 @@
|
||||
#define CSR_MIF 0x02
|
||||
#define CSR_RXAK 0x01
|
||||
|
||||
enum mpc_i2c_action {
|
||||
MPC_I2C_ACTION_START = 1,
|
||||
MPC_I2C_ACTION_RESTART,
|
||||
MPC_I2C_ACTION_READ_BEGIN,
|
||||
MPC_I2C_ACTION_READ_BYTE,
|
||||
MPC_I2C_ACTION_WRITE,
|
||||
MPC_I2C_ACTION_STOP,
|
||||
|
||||
__MPC_I2C_ACTION_CNT
|
||||
};
|
||||
|
||||
static const char * const action_str[] = {
|
||||
"invalid",
|
||||
"start",
|
||||
"restart",
|
||||
"read begin",
|
||||
"read",
|
||||
"write",
|
||||
"stop",
|
||||
};
|
||||
|
||||
static_assert(ARRAY_SIZE(action_str) == __MPC_I2C_ACTION_CNT);
|
||||
|
||||
struct mpc_i2c {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
u32 interrupt;
|
||||
wait_queue_head_t queue;
|
||||
wait_queue_head_t waitq;
|
||||
spinlock_t lock;
|
||||
struct i2c_adapter adap;
|
||||
int irq;
|
||||
u32 real_clk;
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
u8 fdr, dfsrr;
|
||||
#endif
|
||||
struct clk *clk_per;
|
||||
u32 cntl_bits;
|
||||
enum mpc_i2c_action action;
|
||||
struct i2c_msg *msgs;
|
||||
int num_msgs;
|
||||
int curr_msg;
|
||||
u32 byte_posn;
|
||||
u32 block;
|
||||
int rc;
|
||||
int expect_rxack;
|
||||
|
||||
};
|
||||
|
||||
struct mpc_i2c_divider {
|
||||
@ -86,19 +114,6 @@ static inline void writeccr(struct mpc_i2c *i2c, u32 x)
|
||||
writeb(x, i2c->base + MPC_I2C_CR);
|
||||
}
|
||||
|
||||
static irqreturn_t mpc_i2c_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct mpc_i2c *i2c = dev_id;
|
||||
if (readb(i2c->base + MPC_I2C_SR) & CSR_MIF) {
|
||||
/* Read again to allow register to stabilise */
|
||||
i2c->interrupt = readb(i2c->base + MPC_I2C_SR);
|
||||
writeb(0, i2c->base + MPC_I2C_SR);
|
||||
wake_up(&i2c->queue);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/* Sometimes 9th clock pulse isn't generated, and slave doesn't release
|
||||
* the bus, because it wants to send ACK.
|
||||
* Following sequence of enabling/disabling and sending start/stop generates
|
||||
@ -121,61 +136,6 @@ static void mpc_i2c_fixup(struct mpc_i2c *i2c)
|
||||
}
|
||||
}
|
||||
|
||||
static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing)
|
||||
{
|
||||
unsigned long orig_jiffies = jiffies;
|
||||
u32 cmd_err;
|
||||
int result = 0;
|
||||
|
||||
if (!i2c->irq) {
|
||||
while (!(readb(i2c->base + MPC_I2C_SR) & CSR_MIF)) {
|
||||
schedule();
|
||||
if (time_after(jiffies, orig_jiffies + timeout)) {
|
||||
dev_dbg(i2c->dev, "timeout\n");
|
||||
writeccr(i2c, 0);
|
||||
result = -ETIMEDOUT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
cmd_err = readb(i2c->base + MPC_I2C_SR);
|
||||
writeb(0, i2c->base + MPC_I2C_SR);
|
||||
} else {
|
||||
/* Interrupt mode */
|
||||
result = wait_event_timeout(i2c->queue,
|
||||
(i2c->interrupt & CSR_MIF), timeout);
|
||||
|
||||
if (unlikely(!(i2c->interrupt & CSR_MIF))) {
|
||||
dev_dbg(i2c->dev, "wait timeout\n");
|
||||
writeccr(i2c, 0);
|
||||
result = -ETIMEDOUT;
|
||||
}
|
||||
|
||||
cmd_err = i2c->interrupt;
|
||||
i2c->interrupt = 0;
|
||||
}
|
||||
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
if (!(cmd_err & CSR_MCF)) {
|
||||
dev_dbg(i2c->dev, "unfinished\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (cmd_err & CSR_MAL) {
|
||||
dev_dbg(i2c->dev, "MAL\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (writing && (cmd_err & CSR_RXAK)) {
|
||||
dev_dbg(i2c->dev, "No RXAK\n");
|
||||
/* generate stop */
|
||||
writeccr(i2c, CCR_MEN);
|
||||
return -ENXIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_PPC_MPC52xx) || defined(CONFIG_PPC_MPC512x)
|
||||
static const struct mpc_i2c_divider mpc_i2c_dividers_52xx[] = {
|
||||
{20, 0x20}, {22, 0x21}, {24, 0x22}, {26, 0x23},
|
||||
@ -415,7 +375,7 @@ static int mpc_i2c_get_fdr_8xxx(struct device_node *node, u32 clock,
|
||||
}
|
||||
|
||||
*real_clk = fsl_get_sys_freq() / prescaler / div->divider;
|
||||
return div ? (int)div->fdr : -EINVAL;
|
||||
return (int)div->fdr;
|
||||
}
|
||||
|
||||
static void mpc_i2c_setup_8xxx(struct device_node *node,
|
||||
@ -450,168 +410,209 @@ static void mpc_i2c_setup_8xxx(struct device_node *node,
|
||||
}
|
||||
#endif /* CONFIG_FSL_SOC */
|
||||
|
||||
static void mpc_i2c_start(struct mpc_i2c *i2c)
|
||||
static void mpc_i2c_finish(struct mpc_i2c *i2c, int rc)
|
||||
{
|
||||
/* Clear arbitration */
|
||||
writeb(0, i2c->base + MPC_I2C_SR);
|
||||
/* Start with MEN */
|
||||
writeccr(i2c, CCR_MEN);
|
||||
i2c->rc = rc;
|
||||
i2c->block = 0;
|
||||
i2c->cntl_bits = CCR_MEN;
|
||||
writeccr(i2c, i2c->cntl_bits);
|
||||
wake_up(&i2c->waitq);
|
||||
}
|
||||
|
||||
static void mpc_i2c_stop(struct mpc_i2c *i2c)
|
||||
static void mpc_i2c_do_action(struct mpc_i2c *i2c)
|
||||
{
|
||||
writeccr(i2c, CCR_MEN);
|
||||
}
|
||||
struct i2c_msg *msg = &i2c->msgs[i2c->curr_msg];
|
||||
int dir = 0;
|
||||
int recv_len = 0;
|
||||
u8 byte;
|
||||
|
||||
static int mpc_write(struct mpc_i2c *i2c, int target,
|
||||
const u8 *data, int length, int restart)
|
||||
{
|
||||
int i, result;
|
||||
unsigned timeout = i2c->adap.timeout;
|
||||
u32 flags = restart ? CCR_RSTA : 0;
|
||||
dev_dbg(i2c->dev, "action = %s\n", action_str[i2c->action]);
|
||||
|
||||
/* Start as master */
|
||||
writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX | flags);
|
||||
/* Write target byte */
|
||||
writeb((target << 1), i2c->base + MPC_I2C_DR);
|
||||
i2c->cntl_bits &= ~(CCR_RSTA | CCR_MTX | CCR_TXAK);
|
||||
|
||||
result = i2c_wait(i2c, timeout, 1);
|
||||
if (result < 0)
|
||||
return result;
|
||||
if (msg->flags & I2C_M_RD)
|
||||
dir = 1;
|
||||
if (msg->flags & I2C_M_RECV_LEN)
|
||||
recv_len = 1;
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
/* Write data byte */
|
||||
writeb(data[i], i2c->base + MPC_I2C_DR);
|
||||
switch (i2c->action) {
|
||||
case MPC_I2C_ACTION_RESTART:
|
||||
i2c->cntl_bits |= CCR_RSTA;
|
||||
fallthrough;
|
||||
|
||||
result = i2c_wait(i2c, timeout, 1);
|
||||
if (result < 0)
|
||||
return result;
|
||||
}
|
||||
case MPC_I2C_ACTION_START:
|
||||
i2c->cntl_bits |= CCR_MSTA | CCR_MTX;
|
||||
writeccr(i2c, i2c->cntl_bits);
|
||||
writeb((msg->addr << 1) | dir, i2c->base + MPC_I2C_DR);
|
||||
i2c->expect_rxack = 1;
|
||||
i2c->action = dir ? MPC_I2C_ACTION_READ_BEGIN : MPC_I2C_ACTION_WRITE;
|
||||
break;
|
||||
|
||||
return 0;
|
||||
}
|
||||
case MPC_I2C_ACTION_READ_BEGIN:
|
||||
if (msg->len) {
|
||||
if (msg->len == 1 && !(msg->flags & I2C_M_RECV_LEN))
|
||||
i2c->cntl_bits |= CCR_TXAK;
|
||||
|
||||
static int mpc_read(struct mpc_i2c *i2c, int target,
|
||||
u8 *data, int length, int restart, bool recv_len)
|
||||
{
|
||||
unsigned timeout = i2c->adap.timeout;
|
||||
int i, result;
|
||||
u32 flags = restart ? CCR_RSTA : 0;
|
||||
writeccr(i2c, i2c->cntl_bits);
|
||||
/* Dummy read */
|
||||
readb(i2c->base + MPC_I2C_DR);
|
||||
}
|
||||
i2c->action = MPC_I2C_ACTION_READ_BYTE;
|
||||
break;
|
||||
|
||||
/* Switch to read - restart */
|
||||
writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX | flags);
|
||||
/* Write target address byte - this time with the read flag set */
|
||||
writeb((target << 1) | 1, i2c->base + MPC_I2C_DR);
|
||||
|
||||
result = i2c_wait(i2c, timeout, 1);
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
if (length) {
|
||||
if (length == 1 && !recv_len)
|
||||
writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK);
|
||||
else
|
||||
writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA);
|
||||
/* Dummy read */
|
||||
readb(i2c->base + MPC_I2C_DR);
|
||||
}
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
u8 byte;
|
||||
|
||||
result = i2c_wait(i2c, timeout, 0);
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
/*
|
||||
* For block reads, we have to know the total length (1st byte)
|
||||
* before we can determine if we are done.
|
||||
*/
|
||||
if (i || !recv_len) {
|
||||
/* Generate txack on next to last byte */
|
||||
if (i == length - 2)
|
||||
writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA
|
||||
| CCR_TXAK);
|
||||
case MPC_I2C_ACTION_READ_BYTE:
|
||||
if (i2c->byte_posn || !recv_len) {
|
||||
/* Generate Tx ACK on next to last byte */
|
||||
if (i2c->byte_posn == msg->len - 2)
|
||||
i2c->cntl_bits |= CCR_TXAK;
|
||||
/* Do not generate stop on last byte */
|
||||
if (i == length - 1)
|
||||
writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA
|
||||
| CCR_MTX);
|
||||
if (i2c->byte_posn == msg->len - 1)
|
||||
i2c->cntl_bits |= CCR_MTX;
|
||||
|
||||
writeccr(i2c, i2c->cntl_bits);
|
||||
}
|
||||
|
||||
byte = readb(i2c->base + MPC_I2C_DR);
|
||||
|
||||
/*
|
||||
* Adjust length if first received byte is length.
|
||||
* The length is 1 length byte plus actually data length
|
||||
*/
|
||||
if (i == 0 && recv_len) {
|
||||
if (byte == 0 || byte > I2C_SMBUS_BLOCK_MAX)
|
||||
return -EPROTO;
|
||||
length += byte;
|
||||
if (i2c->byte_posn == 0 && recv_len) {
|
||||
if (byte == 0 || byte > I2C_SMBUS_BLOCK_MAX) {
|
||||
mpc_i2c_finish(i2c, -EPROTO);
|
||||
return;
|
||||
}
|
||||
msg->len += byte;
|
||||
/*
|
||||
* For block reads, generate txack here if data length
|
||||
* For block reads, generate Tx ACK here if data length
|
||||
* is 1 byte (total length is 2 bytes).
|
||||
*/
|
||||
if (length == 2)
|
||||
writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA
|
||||
| CCR_TXAK);
|
||||
if (msg->len == 2) {
|
||||
i2c->cntl_bits |= CCR_TXAK;
|
||||
writeccr(i2c, i2c->cntl_bits);
|
||||
}
|
||||
}
|
||||
data[i] = byte;
|
||||
|
||||
dev_dbg(i2c->dev, "%s %02x\n", action_str[i2c->action], byte);
|
||||
msg->buf[i2c->byte_posn++] = byte;
|
||||
break;
|
||||
|
||||
case MPC_I2C_ACTION_WRITE:
|
||||
dev_dbg(i2c->dev, "%s %02x\n", action_str[i2c->action],
|
||||
msg->buf[i2c->byte_posn]);
|
||||
writeb(msg->buf[i2c->byte_posn++], i2c->base + MPC_I2C_DR);
|
||||
i2c->expect_rxack = 1;
|
||||
break;
|
||||
|
||||
case MPC_I2C_ACTION_STOP:
|
||||
mpc_i2c_finish(i2c, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
WARN(1, "Unexpected action %d\n", i2c->action);
|
||||
break;
|
||||
}
|
||||
|
||||
return length;
|
||||
if (msg->len == i2c->byte_posn) {
|
||||
i2c->curr_msg++;
|
||||
i2c->byte_posn = 0;
|
||||
|
||||
if (i2c->curr_msg == i2c->num_msgs) {
|
||||
i2c->action = MPC_I2C_ACTION_STOP;
|
||||
/*
|
||||
* We don't get another interrupt on read so
|
||||
* finish the transfer now
|
||||
*/
|
||||
if (dir)
|
||||
mpc_i2c_finish(i2c, 0);
|
||||
} else {
|
||||
i2c->action = MPC_I2C_ACTION_RESTART;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
||||
static void mpc_i2c_do_intr(struct mpc_i2c *i2c, u8 status)
|
||||
{
|
||||
struct i2c_msg *pmsg;
|
||||
int i;
|
||||
int ret = 0;
|
||||
unsigned long orig_jiffies = jiffies;
|
||||
struct mpc_i2c *i2c = i2c_get_adapdata(adap);
|
||||
spin_lock(&i2c->lock);
|
||||
|
||||
mpc_i2c_start(i2c);
|
||||
|
||||
/* Allow bus up to 1s to become not busy */
|
||||
while (readb(i2c->base + MPC_I2C_SR) & CSR_MBB) {
|
||||
if (signal_pending(current)) {
|
||||
dev_dbg(i2c->dev, "Interrupted\n");
|
||||
writeccr(i2c, 0);
|
||||
return -EINTR;
|
||||
}
|
||||
if (time_after(jiffies, orig_jiffies + HZ)) {
|
||||
u8 status = readb(i2c->base + MPC_I2C_SR);
|
||||
|
||||
dev_dbg(i2c->dev, "timeout\n");
|
||||
if ((status & (CSR_MCF | CSR_MBB | CSR_RXAK)) != 0) {
|
||||
writeb(status & ~CSR_MAL,
|
||||
i2c->base + MPC_I2C_SR);
|
||||
mpc_i2c_fixup(i2c);
|
||||
}
|
||||
return -EIO;
|
||||
}
|
||||
schedule();
|
||||
if (!(status & CSR_MCF)) {
|
||||
dev_dbg(i2c->dev, "unfinished\n");
|
||||
mpc_i2c_finish(i2c, -EIO);
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; ret >= 0 && i < num; i++) {
|
||||
pmsg = &msgs[i];
|
||||
dev_dbg(i2c->dev,
|
||||
"Doing %s %d bytes to 0x%02x - %d of %d messages\n",
|
||||
pmsg->flags & I2C_M_RD ? "read" : "write",
|
||||
pmsg->len, pmsg->addr, i + 1, num);
|
||||
if (pmsg->flags & I2C_M_RD) {
|
||||
bool recv_len = pmsg->flags & I2C_M_RECV_LEN;
|
||||
|
||||
ret = mpc_read(i2c, pmsg->addr, pmsg->buf, pmsg->len, i,
|
||||
recv_len);
|
||||
if (recv_len && ret > 0)
|
||||
pmsg->len = ret;
|
||||
} else {
|
||||
ret =
|
||||
mpc_write(i2c, pmsg->addr, pmsg->buf, pmsg->len, i);
|
||||
}
|
||||
if (status & CSR_MAL) {
|
||||
dev_dbg(i2c->dev, "arbitration lost\n");
|
||||
mpc_i2c_finish(i2c, -EAGAIN);
|
||||
goto out;
|
||||
}
|
||||
mpc_i2c_stop(i2c); /* Initiate STOP */
|
||||
|
||||
if (i2c->expect_rxack && (status & CSR_RXAK)) {
|
||||
dev_dbg(i2c->dev, "no Rx ACK\n");
|
||||
mpc_i2c_finish(i2c, -ENXIO);
|
||||
goto out;
|
||||
}
|
||||
i2c->expect_rxack = 0;
|
||||
|
||||
mpc_i2c_do_action(i2c);
|
||||
|
||||
out:
|
||||
spin_unlock(&i2c->lock);
|
||||
}
|
||||
|
||||
static irqreturn_t mpc_i2c_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct mpc_i2c *i2c = dev_id;
|
||||
u8 status;
|
||||
|
||||
status = readb(i2c->base + MPC_I2C_SR);
|
||||
if (status & CSR_MIF) {
|
||||
writeb(0, i2c->base + MPC_I2C_SR);
|
||||
mpc_i2c_do_intr(i2c, status);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static int mpc_i2c_wait_for_completion(struct mpc_i2c *i2c)
|
||||
{
|
||||
long time_left;
|
||||
|
||||
time_left = wait_event_timeout(i2c->waitq, !i2c->block, i2c->adap.timeout);
|
||||
if (!time_left)
|
||||
return -ETIMEDOUT;
|
||||
if (time_left < 0)
|
||||
return time_left;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpc_i2c_execute_msg(struct mpc_i2c *i2c)
|
||||
{
|
||||
unsigned long orig_jiffies;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&i2c->lock, flags);
|
||||
|
||||
i2c->curr_msg = 0;
|
||||
i2c->rc = 0;
|
||||
i2c->byte_posn = 0;
|
||||
i2c->block = 1;
|
||||
i2c->action = MPC_I2C_ACTION_START;
|
||||
|
||||
i2c->cntl_bits = CCR_MEN | CCR_MIEN;
|
||||
writeb(0, i2c->base + MPC_I2C_SR);
|
||||
writeccr(i2c, i2c->cntl_bits);
|
||||
|
||||
mpc_i2c_do_action(i2c);
|
||||
|
||||
spin_unlock_irqrestore(&i2c->lock, flags);
|
||||
|
||||
ret = mpc_i2c_wait_for_completion(i2c);
|
||||
if (ret)
|
||||
i2c->rc = ret;
|
||||
|
||||
if (i2c->rc == -EIO || i2c->rc == -EAGAIN || i2c->rc == -ETIMEDOUT)
|
||||
i2c_recover_bus(&i2c->adap);
|
||||
|
||||
orig_jiffies = jiffies;
|
||||
/* Wait until STOP is seen, allow up to 1 s */
|
||||
while (readb(i2c->base + MPC_I2C_SR) & CSR_MBB) {
|
||||
@ -622,13 +623,41 @@ static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
||||
if ((status & (CSR_MCF | CSR_MBB | CSR_RXAK)) != 0) {
|
||||
writeb(status & ~CSR_MAL,
|
||||
i2c->base + MPC_I2C_SR);
|
||||
mpc_i2c_fixup(i2c);
|
||||
i2c_recover_bus(&i2c->adap);
|
||||
}
|
||||
return -EIO;
|
||||
}
|
||||
cond_resched();
|
||||
}
|
||||
return (ret < 0) ? ret : num;
|
||||
|
||||
return i2c->rc;
|
||||
}
|
||||
|
||||
static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
||||
{
|
||||
int rc, ret = num;
|
||||
struct mpc_i2c *i2c = i2c_get_adapdata(adap);
|
||||
int i;
|
||||
|
||||
dev_dbg(i2c->dev, "num = %d\n", num);
|
||||
for (i = 0; i < num; i++)
|
||||
dev_dbg(i2c->dev, " addr = %02x, flags = %02x, len = %d, %*ph\n",
|
||||
msgs[i].addr, msgs[i].flags, msgs[i].len,
|
||||
msgs[i].flags & I2C_M_RD ? 0 : msgs[i].len,
|
||||
msgs[i].buf);
|
||||
|
||||
WARN_ON(i2c->msgs != NULL);
|
||||
i2c->msgs = msgs;
|
||||
i2c->num_msgs = num;
|
||||
|
||||
rc = mpc_i2c_execute_msg(i2c);
|
||||
if (rc < 0)
|
||||
ret = rc;
|
||||
|
||||
i2c->num_msgs = 0;
|
||||
i2c->msgs = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u32 mpc_functionality(struct i2c_adapter *adap)
|
||||
@ -637,6 +666,15 @@ static u32 mpc_functionality(struct i2c_adapter *adap)
|
||||
| I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL;
|
||||
}
|
||||
|
||||
static int fsl_i2c_bus_recovery(struct i2c_adapter *adap)
|
||||
{
|
||||
struct mpc_i2c *i2c = i2c_get_adapdata(adap);
|
||||
|
||||
mpc_i2c_fixup(i2c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm mpc_algo = {
|
||||
.master_xfer = mpc_xfer,
|
||||
.functionality = mpc_functionality,
|
||||
@ -648,63 +686,61 @@ static struct i2c_adapter mpc_ops = {
|
||||
.timeout = HZ,
|
||||
};
|
||||
|
||||
static const struct of_device_id mpc_i2c_of_match[];
|
||||
static struct i2c_bus_recovery_info fsl_i2c_recovery_info = {
|
||||
.recover_bus = fsl_i2c_bus_recovery,
|
||||
};
|
||||
|
||||
static int fsl_i2c_probe(struct platform_device *op)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
const struct mpc_i2c_data *data;
|
||||
struct mpc_i2c *i2c;
|
||||
const u32 *prop;
|
||||
u32 clock = MPC_I2C_CLOCK_LEGACY;
|
||||
int result = 0;
|
||||
int plen;
|
||||
struct resource res;
|
||||
struct clk *clk;
|
||||
int err;
|
||||
|
||||
match = of_match_device(mpc_i2c_of_match, &op->dev);
|
||||
if (!match)
|
||||
return -EINVAL;
|
||||
|
||||
i2c = kzalloc(sizeof(*i2c), GFP_KERNEL);
|
||||
i2c = devm_kzalloc(&op->dev, sizeof(*i2c), GFP_KERNEL);
|
||||
if (!i2c)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c->dev = &op->dev; /* for debug and error output */
|
||||
|
||||
init_waitqueue_head(&i2c->queue);
|
||||
init_waitqueue_head(&i2c->waitq);
|
||||
spin_lock_init(&i2c->lock);
|
||||
|
||||
i2c->base = of_iomap(op->dev.of_node, 0);
|
||||
if (!i2c->base) {
|
||||
dev_err(i2c->dev, "failed to map controller\n");
|
||||
result = -ENOMEM;
|
||||
goto fail_map;
|
||||
}
|
||||
i2c->base = devm_platform_ioremap_resource(op, 0);
|
||||
if (IS_ERR(i2c->base))
|
||||
return PTR_ERR(i2c->base);
|
||||
|
||||
i2c->irq = irq_of_parse_and_map(op->dev.of_node, 0);
|
||||
if (i2c->irq) { /* no i2c->irq implies polling */
|
||||
result = request_irq(i2c->irq, mpc_i2c_isr,
|
||||
IRQF_SHARED, "i2c-mpc", i2c);
|
||||
if (result < 0) {
|
||||
dev_err(i2c->dev, "failed to attach interrupt\n");
|
||||
goto fail_request;
|
||||
}
|
||||
i2c->irq = platform_get_irq(op, 0);
|
||||
if (i2c->irq < 0)
|
||||
return i2c->irq;
|
||||
|
||||
result = devm_request_irq(&op->dev, i2c->irq, mpc_i2c_isr,
|
||||
IRQF_SHARED, "i2c-mpc", i2c);
|
||||
if (result < 0) {
|
||||
dev_err(i2c->dev, "failed to attach interrupt\n");
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* enable clock for the I2C peripheral (non fatal),
|
||||
* keep a reference upon successful allocation
|
||||
*/
|
||||
clk = devm_clk_get(&op->dev, NULL);
|
||||
if (!IS_ERR(clk)) {
|
||||
err = clk_prepare_enable(clk);
|
||||
if (err) {
|
||||
dev_err(&op->dev, "failed to enable clock\n");
|
||||
goto fail_request;
|
||||
} else {
|
||||
i2c->clk_per = clk;
|
||||
}
|
||||
clk = devm_clk_get_optional(&op->dev, NULL);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
err = clk_prepare_enable(clk);
|
||||
if (err) {
|
||||
dev_err(&op->dev, "failed to enable clock\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
i2c->clk_per = clk;
|
||||
|
||||
if (of_property_read_bool(op->dev.of_node, "fsl,preserve-clocking")) {
|
||||
clock = MPC_I2C_CLOCK_PRESERVE;
|
||||
} else {
|
||||
@ -714,8 +750,8 @@ static int fsl_i2c_probe(struct platform_device *op)
|
||||
clock = *prop;
|
||||
}
|
||||
|
||||
if (match->data) {
|
||||
const struct mpc_i2c_data *data = match->data;
|
||||
data = device_get_match_data(&op->dev);
|
||||
if (data) {
|
||||
data->setup(op->dev.of_node, i2c, clock);
|
||||
} else {
|
||||
/* Backwards compatibility */
|
||||
@ -731,31 +767,25 @@ static int fsl_i2c_probe(struct platform_device *op)
|
||||
}
|
||||
dev_info(i2c->dev, "timeout %u us\n", mpc_ops.timeout * 1000000 / HZ);
|
||||
|
||||
platform_set_drvdata(op, i2c);
|
||||
|
||||
i2c->adap = mpc_ops;
|
||||
of_address_to_resource(op->dev.of_node, 0, &res);
|
||||
scnprintf(i2c->adap.name, sizeof(i2c->adap.name),
|
||||
"MPC adapter at 0x%llx", (unsigned long long)res.start);
|
||||
i2c_set_adapdata(&i2c->adap, i2c);
|
||||
"MPC adapter (%s)", of_node_full_name(op->dev.of_node));
|
||||
i2c->adap.dev.parent = &op->dev;
|
||||
i2c->adap.nr = op->id;
|
||||
i2c->adap.dev.of_node = of_node_get(op->dev.of_node);
|
||||
i2c->adap.bus_recovery_info = &fsl_i2c_recovery_info;
|
||||
platform_set_drvdata(op, i2c);
|
||||
i2c_set_adapdata(&i2c->adap, i2c);
|
||||
|
||||
result = i2c_add_adapter(&i2c->adap);
|
||||
if (result < 0)
|
||||
result = i2c_add_numbered_adapter(&i2c->adap);
|
||||
if (result)
|
||||
goto fail_add;
|
||||
|
||||
return result;
|
||||
return 0;
|
||||
|
||||
fail_add:
|
||||
if (i2c->clk_per)
|
||||
clk_disable_unprepare(i2c->clk_per);
|
||||
free_irq(i2c->irq, i2c);
|
||||
fail_request:
|
||||
irq_dispose_mapping(i2c->irq);
|
||||
iounmap(i2c->base);
|
||||
fail_map:
|
||||
kfree(i2c);
|
||||
clk_disable_unprepare(i2c->clk_per);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
@ -765,20 +795,12 @@ static int fsl_i2c_remove(struct platform_device *op)
|
||||
|
||||
i2c_del_adapter(&i2c->adap);
|
||||
|
||||
if (i2c->clk_per)
|
||||
clk_disable_unprepare(i2c->clk_per);
|
||||
clk_disable_unprepare(i2c->clk_per);
|
||||
|
||||
if (i2c->irq)
|
||||
free_irq(i2c->irq, i2c);
|
||||
|
||||
irq_dispose_mapping(i2c->irq);
|
||||
iounmap(i2c->base);
|
||||
kfree(i2c);
|
||||
return 0;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mpc_i2c_suspend(struct device *dev)
|
||||
static int __maybe_unused mpc_i2c_suspend(struct device *dev)
|
||||
{
|
||||
struct mpc_i2c *i2c = dev_get_drvdata(dev);
|
||||
|
||||
@ -788,7 +810,7 @@ static int mpc_i2c_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpc_i2c_resume(struct device *dev)
|
||||
static int __maybe_unused mpc_i2c_resume(struct device *dev)
|
||||
{
|
||||
struct mpc_i2c *i2c = dev_get_drvdata(dev);
|
||||
|
||||
@ -797,12 +819,7 @@ static int mpc_i2c_resume(struct device *dev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(mpc_i2c_pm_ops, mpc_i2c_suspend, mpc_i2c_resume);
|
||||
#define MPC_I2C_PM_OPS (&mpc_i2c_pm_ops)
|
||||
#else
|
||||
#define MPC_I2C_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static const struct mpc_i2c_data mpc_i2c_data_512x = {
|
||||
.setup = mpc_i2c_setup_512x,
|
||||
@ -845,7 +862,7 @@ static struct platform_driver mpc_i2c_driver = {
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.of_match_table = mpc_i2c_of_match,
|
||||
.pm = MPC_I2C_PM_OPS,
|
||||
.pm = &mpc_i2c_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -231,6 +231,7 @@ struct mtk_i2c {
|
||||
struct i2c_adapter adap; /* i2c host adapter */
|
||||
struct device *dev;
|
||||
struct completion msg_complete;
|
||||
struct i2c_timings timing_info;
|
||||
|
||||
/* set in i2c probe */
|
||||
void __iomem *base; /* i2c base addr */
|
||||
@ -479,7 +480,7 @@ static void mtk_i2c_init_hw(struct mtk_i2c *i2c)
|
||||
{
|
||||
u16 control_reg;
|
||||
|
||||
if (i2c->dev_comp->dma_sync) {
|
||||
if (i2c->dev_comp->apdma_sync) {
|
||||
writel(I2C_DMA_WARM_RST, i2c->pdmabase + OFFSET_RST);
|
||||
udelay(10);
|
||||
writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_RST);
|
||||
@ -564,7 +565,7 @@ static const struct i2c_spec_values *mtk_i2c_get_spec(unsigned int speed)
|
||||
|
||||
static int mtk_i2c_max_step_cnt(unsigned int target_speed)
|
||||
{
|
||||
if (target_speed > I2C_MAX_FAST_MODE_FREQ)
|
||||
if (target_speed > I2C_MAX_FAST_MODE_PLUS_FREQ)
|
||||
return MAX_HS_STEP_CNT_DIV;
|
||||
else
|
||||
return MAX_STEP_CNT_DIV;
|
||||
@ -607,7 +608,8 @@ static int mtk_i2c_check_ac_timing(struct mtk_i2c *i2c,
|
||||
else
|
||||
clk_ns = sample_ns / 2;
|
||||
|
||||
su_sta_cnt = DIV_ROUND_UP(spec->min_su_sta_ns, clk_ns);
|
||||
su_sta_cnt = DIV_ROUND_UP(spec->min_su_sta_ns +
|
||||
i2c->timing_info.scl_int_delay_ns, clk_ns);
|
||||
if (su_sta_cnt > max_sta_cnt)
|
||||
return -1;
|
||||
|
||||
@ -635,7 +637,7 @@ static int mtk_i2c_check_ac_timing(struct mtk_i2c *i2c,
|
||||
if (sda_min > sda_max)
|
||||
return -3;
|
||||
|
||||
if (check_speed > I2C_MAX_FAST_MODE_FREQ) {
|
||||
if (check_speed > I2C_MAX_FAST_MODE_PLUS_FREQ) {
|
||||
if (i2c->dev_comp->ltiming_adjust) {
|
||||
i2c->ac_timing.hs = I2C_TIME_DEFAULT_VALUE |
|
||||
(sample_cnt << 12) | (high_cnt << 8);
|
||||
@ -850,7 +852,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
|
||||
|
||||
control_reg = mtk_i2c_readw(i2c, OFFSET_CONTROL) &
|
||||
~(I2C_CONTROL_DIR_CHANGE | I2C_CONTROL_RS);
|
||||
if ((i2c->speed_hz > I2C_MAX_FAST_MODE_FREQ) || (left_num >= 1))
|
||||
if ((i2c->speed_hz > I2C_MAX_FAST_MODE_PLUS_FREQ) || (left_num >= 1))
|
||||
control_reg |= I2C_CONTROL_RS;
|
||||
|
||||
if (i2c->op == I2C_MASTER_WRRD)
|
||||
@ -1067,7 +1069,8 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap,
|
||||
}
|
||||
}
|
||||
|
||||
if (i2c->auto_restart && num >= 2 && i2c->speed_hz > I2C_MAX_FAST_MODE_FREQ)
|
||||
if (i2c->auto_restart && num >= 2 &&
|
||||
i2c->speed_hz > I2C_MAX_FAST_MODE_PLUS_FREQ)
|
||||
/* ignore the first restart irq after the master code,
|
||||
* otherwise the first transfer will be discarded.
|
||||
*/
|
||||
@ -1175,6 +1178,8 @@ static int mtk_i2c_parse_dt(struct device_node *np, struct mtk_i2c *i2c)
|
||||
i2c->use_push_pull =
|
||||
of_property_read_bool(np, "mediatek,use-push-pull");
|
||||
|
||||
i2c_parse_fw_timings(i2c->dev, &i2c->timing_info, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -277,7 +277,7 @@ static int init_hw(struct nmk_i2c_dev *dev)
|
||||
goto exit;
|
||||
|
||||
/* disable the controller */
|
||||
i2c_clr_bit(dev->virtbase + I2C_CR , I2C_CR_PE);
|
||||
i2c_clr_bit(dev->virtbase + I2C_CR, I2C_CR_PE);
|
||||
|
||||
disable_all_interrupts(dev);
|
||||
|
||||
@ -525,7 +525,7 @@ static int write_i2c(struct nmk_i2c_dev *dev, u16 flags)
|
||||
dev->virtbase + I2C_CR);
|
||||
|
||||
/* enable the controller */
|
||||
i2c_set_bit(dev->virtbase + I2C_CR , I2C_CR_PE);
|
||||
i2c_set_bit(dev->virtbase + I2C_CR, I2C_CR_PE);
|
||||
|
||||
init_completion(&dev->xfer_complete);
|
||||
|
||||
|
@ -262,6 +262,10 @@ static const struct property_entry ccgx_props[] = {
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node ccgx_node = {
|
||||
.properties = ccgx_props,
|
||||
};
|
||||
|
||||
static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq)
|
||||
{
|
||||
i2cd->gpu_ccgx_ucsi = devm_kzalloc(i2cd->dev,
|
||||
@ -274,7 +278,7 @@ static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq)
|
||||
sizeof(i2cd->gpu_ccgx_ucsi->type));
|
||||
i2cd->gpu_ccgx_ucsi->addr = 0x8;
|
||||
i2cd->gpu_ccgx_ucsi->irq = irq;
|
||||
i2cd->gpu_ccgx_ucsi->properties = ccgx_props;
|
||||
i2cd->gpu_ccgx_ucsi->swnode = &ccgx_node;
|
||||
i2cd->ccgx_client = i2c_new_client_device(&i2cd->adapter, i2cd->gpu_ccgx_ucsi);
|
||||
return PTR_ERR_OR_ZERO(i2cd->ccgx_client);
|
||||
}
|
||||
|
@ -1404,9 +1404,9 @@ omap_i2c_probe(struct platform_device *pdev)
|
||||
pm_runtime_set_autosuspend_delay(omap->dev, OMAP_I2C_PM_TIMEOUT);
|
||||
pm_runtime_use_autosuspend(omap->dev);
|
||||
|
||||
r = pm_runtime_get_sync(omap->dev);
|
||||
r = pm_runtime_resume_and_get(omap->dev);
|
||||
if (r < 0)
|
||||
goto err_free_mem;
|
||||
goto err_disable_pm;
|
||||
|
||||
/*
|
||||
* Read the Rev hi bit-[15:14] ie scheme this is 1 indicates ver2.
|
||||
@ -1513,8 +1513,8 @@ err_unuse_clocks:
|
||||
omap_i2c_write_reg(omap, OMAP_I2C_CON_REG, 0);
|
||||
pm_runtime_dont_use_autosuspend(omap->dev);
|
||||
pm_runtime_put_sync(omap->dev);
|
||||
err_disable_pm:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
err_free_mem:
|
||||
|
||||
return r;
|
||||
}
|
||||
@ -1525,7 +1525,7 @@ static int omap_i2c_remove(struct platform_device *pdev)
|
||||
int ret;
|
||||
|
||||
i2c_del_adapter(&omap->adapter);
|
||||
ret = pm_runtime_get_sync(&pdev->dev);
|
||||
ret = pm_runtime_resume_and_get(&pdev->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -76,11 +76,6 @@ static s32 i2c_powermac_smbus_xfer( struct i2c_adapter* adap,
|
||||
* but I think the current API makes no sense and I don't want
|
||||
* any driver that I haven't verified for correctness to go
|
||||
* anywhere near a pmac i2c bus anyway ...
|
||||
*
|
||||
* I'm also not completely sure what kind of phases to do between
|
||||
* the actual command and the data (what I am _supposed_ to do that
|
||||
* is). For now, I assume writes are a single stream and reads have
|
||||
* a repeat start/addr phase (but not stop in between)
|
||||
*/
|
||||
case I2C_SMBUS_BLOCK_DATA:
|
||||
buf = data->block;
|
||||
|
@ -569,9 +569,9 @@ static int cci_probe(struct platform_device *pdev)
|
||||
cci->master[idx].mode = I2C_MODE_STANDARD;
|
||||
ret = of_property_read_u32(child, "clock-frequency", &val);
|
||||
if (!ret) {
|
||||
if (val == 400000)
|
||||
if (val == I2C_MAX_FAST_MODE_FREQ)
|
||||
cci->master[idx].mode = I2C_MODE_FAST;
|
||||
else if (val == 1000000)
|
||||
else if (val == I2C_MAX_FAST_MODE_PLUS_FREQ)
|
||||
cci->master[idx].mode = I2C_MODE_FAST_PLUS;
|
||||
}
|
||||
|
||||
|
@ -141,6 +141,7 @@ struct rcar_i2c_priv {
|
||||
enum dma_data_direction dma_direction;
|
||||
|
||||
struct reset_control *rstc;
|
||||
bool atomic_xfer;
|
||||
int irq;
|
||||
|
||||
struct i2c_client *host_notify_client;
|
||||
@ -353,7 +354,9 @@ static void rcar_i2c_prepare_msg(struct rcar_i2c_priv *priv)
|
||||
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START);
|
||||
rcar_i2c_write(priv, ICMSR, 0);
|
||||
}
|
||||
rcar_i2c_write(priv, ICMIER, read ? RCAR_IRQ_RECV : RCAR_IRQ_SEND);
|
||||
|
||||
if (!priv->atomic_xfer)
|
||||
rcar_i2c_write(priv, ICMIER, read ? RCAR_IRQ_RECV : RCAR_IRQ_SEND);
|
||||
}
|
||||
|
||||
static void rcar_i2c_next_msg(struct rcar_i2c_priv *priv)
|
||||
@ -418,7 +421,7 @@ static bool rcar_i2c_dma(struct rcar_i2c_priv *priv)
|
||||
int len;
|
||||
|
||||
/* Do various checks to see if DMA is feasible at all */
|
||||
if (IS_ERR(chan) || msg->len < RCAR_MIN_DMA_LEN ||
|
||||
if (priv->atomic_xfer || IS_ERR(chan) || msg->len < RCAR_MIN_DMA_LEN ||
|
||||
!(msg->flags & I2C_M_DMA_SAFE) || (read && priv->flags & ID_P_NO_RXDMA))
|
||||
return false;
|
||||
|
||||
@ -646,7 +649,8 @@ static irqreturn_t rcar_i2c_irq(int irq, struct rcar_i2c_priv *priv, u32 msr)
|
||||
/* Nack */
|
||||
if (msr & MNR) {
|
||||
/* HW automatically sends STOP after received NACK */
|
||||
rcar_i2c_write(priv, ICMIER, RCAR_IRQ_STOP);
|
||||
if (!priv->atomic_xfer)
|
||||
rcar_i2c_write(priv, ICMIER, RCAR_IRQ_STOP);
|
||||
priv->flags |= ID_NACK;
|
||||
goto out;
|
||||
}
|
||||
@ -667,7 +671,8 @@ out:
|
||||
if (priv->flags & ID_DONE) {
|
||||
rcar_i2c_write(priv, ICMIER, 0);
|
||||
rcar_i2c_write(priv, ICMSR, 0);
|
||||
wake_up(&priv->wait);
|
||||
if (!priv->atomic_xfer)
|
||||
wake_up(&priv->wait);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
@ -684,7 +689,8 @@ static irqreturn_t rcar_i2c_gen2_irq(int irq, void *ptr)
|
||||
|
||||
/* Only handle interrupts that are currently enabled */
|
||||
msr = rcar_i2c_read(priv, ICMSR);
|
||||
msr &= rcar_i2c_read(priv, ICMIER);
|
||||
if (!priv->atomic_xfer)
|
||||
msr &= rcar_i2c_read(priv, ICMIER);
|
||||
|
||||
return rcar_i2c_irq(irq, priv, msr);
|
||||
}
|
||||
@ -696,7 +702,8 @@ static irqreturn_t rcar_i2c_gen3_irq(int irq, void *ptr)
|
||||
|
||||
/* Only handle interrupts that are currently enabled */
|
||||
msr = rcar_i2c_read(priv, ICMSR);
|
||||
msr &= rcar_i2c_read(priv, ICMIER);
|
||||
if (!priv->atomic_xfer)
|
||||
msr &= rcar_i2c_read(priv, ICMIER);
|
||||
|
||||
/*
|
||||
* Clear START or STOP immediately, except for REPSTART after read or
|
||||
@ -804,6 +811,8 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
|
||||
int i, ret;
|
||||
long time_left;
|
||||
|
||||
priv->atomic_xfer = false;
|
||||
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
/* Check bus state before init otherwise bus busy info will be lost */
|
||||
@ -858,6 +867,68 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rcar_i2c_master_xfer_atomic(struct i2c_adapter *adap,
|
||||
struct i2c_msg *msgs,
|
||||
int num)
|
||||
{
|
||||
struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
|
||||
struct device *dev = rcar_i2c_priv_to_dev(priv);
|
||||
unsigned long j;
|
||||
bool time_left;
|
||||
int ret;
|
||||
|
||||
priv->atomic_xfer = true;
|
||||
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
/* Check bus state before init otherwise bus busy info will be lost */
|
||||
ret = rcar_i2c_bus_barrier(priv);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
rcar_i2c_init(priv);
|
||||
|
||||
/* init first message */
|
||||
priv->msg = msgs;
|
||||
priv->msgs_left = num;
|
||||
priv->flags = (priv->flags & ID_P_MASK) | ID_FIRST_MSG;
|
||||
rcar_i2c_prepare_msg(priv);
|
||||
|
||||
j = jiffies + num * adap->timeout;
|
||||
do {
|
||||
u32 msr = rcar_i2c_read(priv, ICMSR);
|
||||
|
||||
msr &= (rcar_i2c_is_recv(priv) ? RCAR_IRQ_RECV : RCAR_IRQ_SEND) | RCAR_IRQ_STOP;
|
||||
|
||||
if (msr) {
|
||||
if (priv->devtype < I2C_RCAR_GEN3)
|
||||
rcar_i2c_gen2_irq(0, priv);
|
||||
else
|
||||
rcar_i2c_gen3_irq(0, priv);
|
||||
}
|
||||
|
||||
time_left = time_before_eq(jiffies, j);
|
||||
} while (!(priv->flags & ID_DONE) && time_left);
|
||||
|
||||
if (!time_left) {
|
||||
rcar_i2c_init(priv);
|
||||
ret = -ETIMEDOUT;
|
||||
} else if (priv->flags & ID_NACK) {
|
||||
ret = -ENXIO;
|
||||
} else if (priv->flags & ID_ARBLOST) {
|
||||
ret = -EAGAIN;
|
||||
} else {
|
||||
ret = num - priv->msgs_left; /* The number of transfer */
|
||||
}
|
||||
out:
|
||||
pm_runtime_put(dev);
|
||||
|
||||
if (ret < 0 && ret != -ENXIO)
|
||||
dev_err(dev, "error %d : %x\n", ret, priv->flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rcar_reg_slave(struct i2c_client *slave)
|
||||
{
|
||||
struct rcar_i2c_priv *priv = i2c_get_adapdata(slave->adapter);
|
||||
@ -922,6 +993,7 @@ static u32 rcar_i2c_func(struct i2c_adapter *adap)
|
||||
|
||||
static const struct i2c_algorithm rcar_i2c_algo = {
|
||||
.master_xfer = rcar_i2c_master_xfer,
|
||||
.master_xfer_atomic = rcar_i2c_master_xfer_atomic,
|
||||
.functionality = rcar_i2c_func,
|
||||
.reg_slave = rcar_reg_slave,
|
||||
.unreg_slave = rcar_unreg_slave,
|
||||
@ -1027,7 +1099,10 @@ static int rcar_i2c_probe(struct platform_device *pdev)
|
||||
if (of_property_read_bool(dev->of_node, "smbus"))
|
||||
priv->flags |= ID_P_HOST_NOTIFY;
|
||||
|
||||
priv->irq = platform_get_irq(pdev, 0);
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0)
|
||||
goto out_pm_disable;
|
||||
priv->irq = ret;
|
||||
ret = devm_request_irq(dev, priv->irq, irqhandler, irqflags, dev_name(dev), priv);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "cannot get irq %d\n", priv->irq);
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
@ -156,12 +157,8 @@ MODULE_DEVICE_TABLE(of, s3c24xx_i2c_match);
|
||||
*/
|
||||
static inline kernel_ulong_t s3c24xx_get_device_quirks(struct platform_device *pdev)
|
||||
{
|
||||
if (pdev->dev.of_node) {
|
||||
const struct of_device_id *match;
|
||||
|
||||
match = of_match_node(s3c24xx_i2c_match, pdev->dev.of_node);
|
||||
return (kernel_ulong_t)match->data;
|
||||
}
|
||||
if (pdev->dev.of_node)
|
||||
return (kernel_ulong_t)of_device_get_match_data(&pdev->dev);
|
||||
|
||||
return platform_get_device_id(pdev)->driver_data;
|
||||
}
|
||||
|
@ -18,8 +18,6 @@
|
||||
/* SMBUS HID definition as supported by Microsoft Windows */
|
||||
#define ACPI_SMBUS_MS_HID "SMB0001"
|
||||
|
||||
ACPI_MODULE_NAME("smbus_cmi");
|
||||
|
||||
struct smbus_methods_t {
|
||||
char *mt_info;
|
||||
char *mt_sbr;
|
||||
|
@ -471,7 +471,10 @@ static int sh7760_i2c_probe(struct platform_device *pdev)
|
||||
goto out2;
|
||||
}
|
||||
|
||||
id->irq = platform_get_irq(pdev, 0);
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0)
|
||||
goto out3;
|
||||
id->irq = ret;
|
||||
|
||||
id->adap.nr = pdev->id;
|
||||
id->adap.algo = &sh7760_i2c_algo;
|
||||
|
@ -290,7 +290,7 @@ static int sprd_i2c_master_xfer(struct i2c_adapter *i2c_adap,
|
||||
struct sprd_i2c *i2c_dev = i2c_adap->algo_data;
|
||||
int im, ret;
|
||||
|
||||
ret = pm_runtime_get_sync(i2c_dev->dev);
|
||||
ret = pm_runtime_resume_and_get(i2c_dev->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -576,7 +576,7 @@ static int sprd_i2c_remove(struct platform_device *pdev)
|
||||
struct sprd_i2c *i2c_dev = platform_get_drvdata(pdev);
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_get_sync(i2c_dev->dev);
|
||||
ret = pm_runtime_resume_and_get(i2c_dev->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -640,6 +640,7 @@ static const struct of_device_id sprd_i2c_of_match[] = {
|
||||
{ .compatible = "sprd,sc9860-i2c", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sprd_i2c_of_match);
|
||||
|
||||
static struct platform_driver sprd_i2c_driver = {
|
||||
.probe = sprd_i2c_probe,
|
||||
|
@ -164,7 +164,6 @@ enum {
|
||||
#define STM32F7_I2C_DNF_DEFAULT 0
|
||||
#define STM32F7_I2C_DNF_MAX 15
|
||||
|
||||
#define STM32F7_I2C_ANALOG_FILTER_ENABLE 1
|
||||
#define STM32F7_I2C_ANALOG_FILTER_DELAY_MIN 50 /* ns */
|
||||
#define STM32F7_I2C_ANALOG_FILTER_DELAY_MAX 260 /* ns */
|
||||
|
||||
@ -223,8 +222,6 @@ struct stm32f7_i2c_spec {
|
||||
* @clock_src: I2C clock source frequency (Hz)
|
||||
* @rise_time: Rise time (ns)
|
||||
* @fall_time: Fall time (ns)
|
||||
* @dnf: Digital filter coefficient (0-16)
|
||||
* @analog_filter: Analog filter delay (On/Off)
|
||||
* @fmp_clr_offset: Fast Mode Plus clear register offset from set register
|
||||
*/
|
||||
struct stm32f7_i2c_setup {
|
||||
@ -232,8 +229,6 @@ struct stm32f7_i2c_setup {
|
||||
u32 clock_src;
|
||||
u32 rise_time;
|
||||
u32 fall_time;
|
||||
u8 dnf;
|
||||
bool analog_filter;
|
||||
u32 fmp_clr_offset;
|
||||
};
|
||||
|
||||
@ -312,6 +307,9 @@ struct stm32f7_i2c_msg {
|
||||
* @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
|
||||
* @analog_filter: boolean to indicate enabling of the analog filter
|
||||
* @dnf_dt: value of digital filter requested via dt
|
||||
* @dnf: value of digital filter to apply
|
||||
*/
|
||||
struct stm32f7_i2c_dev {
|
||||
struct i2c_adapter adap;
|
||||
@ -340,6 +338,9 @@ struct stm32f7_i2c_dev {
|
||||
bool wakeup_src;
|
||||
bool smbus_mode;
|
||||
struct i2c_client *host_notify_client;
|
||||
bool analog_filter;
|
||||
u32 dnf_dt;
|
||||
u32 dnf;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -385,15 +386,11 @@ static struct stm32f7_i2c_spec stm32f7_i2c_specs[] = {
|
||||
static const struct stm32f7_i2c_setup stm32f7_setup = {
|
||||
.rise_time = STM32F7_I2C_RISE_TIME_DEFAULT,
|
||||
.fall_time = STM32F7_I2C_FALL_TIME_DEFAULT,
|
||||
.dnf = STM32F7_I2C_DNF_DEFAULT,
|
||||
.analog_filter = STM32F7_I2C_ANALOG_FILTER_ENABLE,
|
||||
};
|
||||
|
||||
static const struct stm32f7_i2c_setup stm32mp15_setup = {
|
||||
.rise_time = STM32F7_I2C_RISE_TIME_DEFAULT,
|
||||
.fall_time = STM32F7_I2C_FALL_TIME_DEFAULT,
|
||||
.dnf = STM32F7_I2C_DNF_DEFAULT,
|
||||
.analog_filter = STM32F7_I2C_ANALOG_FILTER_ENABLE,
|
||||
.fmp_clr_offset = 0x40,
|
||||
};
|
||||
|
||||
@ -462,27 +459,28 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (setup->dnf > STM32F7_I2C_DNF_MAX) {
|
||||
i2c_dev->dnf = DIV_ROUND_CLOSEST(i2c_dev->dnf_dt, i2cclk);
|
||||
if (i2c_dev->dnf > STM32F7_I2C_DNF_MAX) {
|
||||
dev_err(i2c_dev->dev,
|
||||
"DNF out of bound %d/%d\n",
|
||||
setup->dnf, STM32F7_I2C_DNF_MAX);
|
||||
i2c_dev->dnf * i2cclk, STM32F7_I2C_DNF_MAX * i2cclk);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Analog and Digital Filters */
|
||||
af_delay_min =
|
||||
(setup->analog_filter ?
|
||||
(i2c_dev->analog_filter ?
|
||||
STM32F7_I2C_ANALOG_FILTER_DELAY_MIN : 0);
|
||||
af_delay_max =
|
||||
(setup->analog_filter ?
|
||||
(i2c_dev->analog_filter ?
|
||||
STM32F7_I2C_ANALOG_FILTER_DELAY_MAX : 0);
|
||||
dnf_delay = setup->dnf * i2cclk;
|
||||
dnf_delay = i2c_dev->dnf * i2cclk;
|
||||
|
||||
sdadel_min = specs->hddat_min + setup->fall_time -
|
||||
af_delay_min - (setup->dnf + 3) * i2cclk;
|
||||
af_delay_min - (i2c_dev->dnf + 3) * i2cclk;
|
||||
|
||||
sdadel_max = specs->vddat_max - setup->rise_time -
|
||||
af_delay_max - (setup->dnf + 4) * i2cclk;
|
||||
af_delay_max - (i2c_dev->dnf + 4) * i2cclk;
|
||||
|
||||
scldel_min = setup->rise_time + specs->sudat_min;
|
||||
|
||||
@ -648,6 +646,7 @@ static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev,
|
||||
setup->speed_freq = t->bus_freq_hz;
|
||||
i2c_dev->setup.rise_time = t->scl_rise_ns;
|
||||
i2c_dev->setup.fall_time = t->scl_fall_ns;
|
||||
i2c_dev->dnf_dt = t->digital_filter_width_ns;
|
||||
setup->clock_src = clk_get_rate(i2c_dev->clk);
|
||||
|
||||
if (!setup->clock_src) {
|
||||
@ -655,6 +654,9 @@ static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!of_property_read_bool(i2c_dev->dev->of_node, "i2c-digital-filter"))
|
||||
i2c_dev->dnf_dt = STM32F7_I2C_DNF_DEFAULT;
|
||||
|
||||
do {
|
||||
ret = stm32f7_i2c_compute_timing(i2c_dev, setup,
|
||||
&i2c_dev->timing);
|
||||
@ -676,12 +678,15 @@ static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
i2c_dev->analog_filter = of_property_read_bool(i2c_dev->dev->of_node,
|
||||
"i2c-analog-filter");
|
||||
|
||||
dev_dbg(i2c_dev->dev, "I2C Speed(%i), Clk Source(%i)\n",
|
||||
setup->speed_freq, setup->clock_src);
|
||||
dev_dbg(i2c_dev->dev, "I2C Rise(%i) and Fall(%i) Time\n",
|
||||
setup->rise_time, setup->fall_time);
|
||||
dev_dbg(i2c_dev->dev, "I2C Analog Filter(%s), DNF(%i)\n",
|
||||
(setup->analog_filter ? "On" : "Off"), setup->dnf);
|
||||
(i2c_dev->analog_filter ? "On" : "Off"), i2c_dev->dnf);
|
||||
|
||||
i2c_dev->bus_rate = setup->speed_freq;
|
||||
|
||||
@ -720,8 +725,8 @@ static void stm32f7_i2c_hw_config(struct stm32f7_i2c_dev *i2c_dev)
|
||||
timing |= STM32F7_I2C_TIMINGR_SCLL(t->scll);
|
||||
writel_relaxed(timing, i2c_dev->base + STM32F7_I2C_TIMINGR);
|
||||
|
||||
/* Enable I2C */
|
||||
if (i2c_dev->setup.analog_filter)
|
||||
/* Configure the Analog Filter */
|
||||
if (i2c_dev->analog_filter)
|
||||
stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1,
|
||||
STM32F7_I2C_CR1_ANFOFF);
|
||||
else
|
||||
@ -732,7 +737,7 @@ static void stm32f7_i2c_hw_config(struct stm32f7_i2c_dev *i2c_dev)
|
||||
stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1,
|
||||
STM32F7_I2C_CR1_DNF_MASK);
|
||||
stm32f7_i2c_set_bits(i2c_dev->base + STM32F7_I2C_CR1,
|
||||
STM32F7_I2C_CR1_DNF(i2c_dev->setup.dnf));
|
||||
STM32F7_I2C_CR1_DNF(i2c_dev->dnf));
|
||||
|
||||
stm32f7_i2c_set_bits(i2c_dev->base + STM32F7_I2C_CR1,
|
||||
STM32F7_I2C_CR1_PE);
|
||||
@ -1597,7 +1602,8 @@ static irqreturn_t stm32f7_i2c_isr_error(int irq, void *data)
|
||||
|
||||
/* Bus error */
|
||||
if (status & STM32F7_I2C_ISR_BERR) {
|
||||
dev_err(dev, "<%s>: Bus error\n", __func__);
|
||||
dev_err(dev, "<%s>: Bus error accessing addr 0x%x\n",
|
||||
__func__, f7_msg->addr);
|
||||
writel_relaxed(STM32F7_I2C_ICR_BERRCF, base + STM32F7_I2C_ICR);
|
||||
stm32f7_i2c_release_bus(&i2c_dev->adap);
|
||||
f7_msg->result = -EIO;
|
||||
@ -1605,13 +1611,15 @@ static irqreturn_t stm32f7_i2c_isr_error(int irq, void *data)
|
||||
|
||||
/* Arbitration loss */
|
||||
if (status & STM32F7_I2C_ISR_ARLO) {
|
||||
dev_dbg(dev, "<%s>: Arbitration loss\n", __func__);
|
||||
dev_dbg(dev, "<%s>: Arbitration loss accessing addr 0x%x\n",
|
||||
__func__, f7_msg->addr);
|
||||
writel_relaxed(STM32F7_I2C_ICR_ARLOCF, base + STM32F7_I2C_ICR);
|
||||
f7_msg->result = -EAGAIN;
|
||||
}
|
||||
|
||||
if (status & STM32F7_I2C_ISR_PECERR) {
|
||||
dev_err(dev, "<%s>: PEC error in reception\n", __func__);
|
||||
dev_err(dev, "<%s>: PEC error in reception accessing addr 0x%x\n",
|
||||
__func__, f7_msg->addr);
|
||||
writel_relaxed(STM32F7_I2C_ICR_PECCF, base + STM32F7_I2C_ICR);
|
||||
f7_msg->result = -EINVAL;
|
||||
}
|
||||
@ -1652,7 +1660,7 @@ static int stm32f7_i2c_xfer(struct i2c_adapter *i2c_adap,
|
||||
i2c_dev->msg_id = 0;
|
||||
f7_msg->smbus = false;
|
||||
|
||||
ret = pm_runtime_get_sync(i2c_dev->dev);
|
||||
ret = pm_runtime_resume_and_get(i2c_dev->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -1698,7 +1706,7 @@ static int stm32f7_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
|
||||
f7_msg->read_write = read_write;
|
||||
f7_msg->smbus = true;
|
||||
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -1799,7 +1807,7 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -1880,7 +1888,7 @@ static int stm32f7_i2c_unreg_slave(struct i2c_client *slave)
|
||||
|
||||
WARN_ON(!i2c_dev->slave[id]);
|
||||
|
||||
ret = pm_runtime_get_sync(i2c_dev->dev);
|
||||
ret = pm_runtime_resume_and_get(i2c_dev->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -2027,12 +2035,8 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
|
||||
phy_addr = (dma_addr_t)res->start;
|
||||
|
||||
irq_event = platform_get_irq(pdev, 0);
|
||||
if (irq_event <= 0) {
|
||||
if (irq_event != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "Failed to get IRQ event: %d\n",
|
||||
irq_event);
|
||||
if (irq_event <= 0)
|
||||
return irq_event ? : -ENOENT;
|
||||
}
|
||||
|
||||
irq_error = platform_get_irq(pdev, 1);
|
||||
if (irq_error <= 0)
|
||||
@ -2267,13 +2271,12 @@ static int __maybe_unused stm32f7_i2c_runtime_resume(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int stm32f7_i2c_regs_backup(struct stm32f7_i2c_dev *i2c_dev)
|
||||
static int __maybe_unused stm32f7_i2c_regs_backup(struct stm32f7_i2c_dev *i2c_dev)
|
||||
{
|
||||
int ret;
|
||||
struct stm32f7_i2c_regs *backup_regs = &i2c_dev->backup_regs;
|
||||
|
||||
ret = pm_runtime_get_sync(i2c_dev->dev);
|
||||
ret = pm_runtime_resume_and_get(i2c_dev->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -2289,13 +2292,13 @@ static int stm32f7_i2c_regs_backup(struct stm32f7_i2c_dev *i2c_dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm32f7_i2c_regs_restore(struct stm32f7_i2c_dev *i2c_dev)
|
||||
static int __maybe_unused stm32f7_i2c_regs_restore(struct stm32f7_i2c_dev *i2c_dev)
|
||||
{
|
||||
u32 cr1;
|
||||
int ret;
|
||||
struct stm32f7_i2c_regs *backup_regs = &i2c_dev->backup_regs;
|
||||
|
||||
ret = pm_runtime_get_sync(i2c_dev->dev);
|
||||
ret = pm_runtime_resume_and_get(i2c_dev->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -2320,7 +2323,7 @@ static int stm32f7_i2c_regs_restore(struct stm32f7_i2c_dev *i2c_dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm32f7_i2c_suspend(struct device *dev)
|
||||
static int __maybe_unused stm32f7_i2c_suspend(struct device *dev)
|
||||
{
|
||||
struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
@ -2341,7 +2344,7 @@ static int stm32f7_i2c_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32f7_i2c_resume(struct device *dev)
|
||||
static int __maybe_unused stm32f7_i2c_resume(struct device *dev)
|
||||
{
|
||||
struct stm32f7_i2c_dev *i2c_dev = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
@ -2361,7 +2364,6 @@ static int stm32f7_i2c_resume(struct device *dev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops stm32f7_i2c_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(stm32f7_i2c_runtime_suspend,
|
||||
|
@ -38,49 +38,31 @@ struct tegra_bpmp_i2c {
|
||||
* firmware I2C driver to avoid any issues in future if Linux I2C flags are
|
||||
* changed.
|
||||
*/
|
||||
static int tegra_bpmp_xlate_flags(u16 flags, u16 *out)
|
||||
static void tegra_bpmp_xlate_flags(u16 flags, u16 *out)
|
||||
{
|
||||
if (flags & I2C_M_TEN) {
|
||||
if (flags & I2C_M_TEN)
|
||||
*out |= SERIALI2C_TEN;
|
||||
flags &= ~I2C_M_TEN;
|
||||
}
|
||||
|
||||
if (flags & I2C_M_RD) {
|
||||
if (flags & I2C_M_RD)
|
||||
*out |= SERIALI2C_RD;
|
||||
flags &= ~I2C_M_RD;
|
||||
}
|
||||
|
||||
if (flags & I2C_M_STOP) {
|
||||
if (flags & I2C_M_STOP)
|
||||
*out |= SERIALI2C_STOP;
|
||||
flags &= ~I2C_M_STOP;
|
||||
}
|
||||
|
||||
if (flags & I2C_M_NOSTART) {
|
||||
if (flags & I2C_M_NOSTART)
|
||||
*out |= SERIALI2C_NOSTART;
|
||||
flags &= ~I2C_M_NOSTART;
|
||||
}
|
||||
|
||||
if (flags & I2C_M_REV_DIR_ADDR) {
|
||||
if (flags & I2C_M_REV_DIR_ADDR)
|
||||
*out |= SERIALI2C_REV_DIR_ADDR;
|
||||
flags &= ~I2C_M_REV_DIR_ADDR;
|
||||
}
|
||||
|
||||
if (flags & I2C_M_IGNORE_NAK) {
|
||||
if (flags & I2C_M_IGNORE_NAK)
|
||||
*out |= SERIALI2C_IGNORE_NAK;
|
||||
flags &= ~I2C_M_IGNORE_NAK;
|
||||
}
|
||||
|
||||
if (flags & I2C_M_NO_RD_ACK) {
|
||||
if (flags & I2C_M_NO_RD_ACK)
|
||||
*out |= SERIALI2C_NO_RD_ACK;
|
||||
flags &= ~I2C_M_NO_RD_ACK;
|
||||
}
|
||||
|
||||
if (flags & I2C_M_RECV_LEN) {
|
||||
if (flags & I2C_M_RECV_LEN)
|
||||
*out |= SERIALI2C_RECV_LEN;
|
||||
flags &= ~I2C_M_RECV_LEN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -97,22 +79,19 @@ static int tegra_bpmp_xlate_flags(u16 flags, u16 *out)
|
||||
*
|
||||
* See deserialize_i2c documentation for the data format in the other direction.
|
||||
*/
|
||||
static int tegra_bpmp_serialize_i2c_msg(struct tegra_bpmp_i2c *i2c,
|
||||
static void tegra_bpmp_serialize_i2c_msg(struct tegra_bpmp_i2c *i2c,
|
||||
struct mrq_i2c_request *request,
|
||||
struct i2c_msg *msgs,
|
||||
unsigned int num)
|
||||
{
|
||||
char *buf = request->xfer.data_buf;
|
||||
unsigned int i, j, pos = 0;
|
||||
int err;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
struct i2c_msg *msg = &msgs[i];
|
||||
u16 flags = 0;
|
||||
|
||||
err = tegra_bpmp_xlate_flags(msg->flags, &flags);
|
||||
if (err < 0)
|
||||
return err;
|
||||
tegra_bpmp_xlate_flags(msg->flags, &flags);
|
||||
|
||||
buf[pos++] = msg->addr & 0xff;
|
||||
buf[pos++] = (msg->addr & 0xff00) >> 8;
|
||||
@ -128,8 +107,6 @@ static int tegra_bpmp_serialize_i2c_msg(struct tegra_bpmp_i2c *i2c,
|
||||
}
|
||||
|
||||
request->xfer.data_size = pos;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -217,7 +194,32 @@ static int tegra_bpmp_i2c_msg_xfer(struct tegra_bpmp_i2c *i2c,
|
||||
else
|
||||
err = tegra_bpmp_transfer(i2c->bpmp, &msg);
|
||||
|
||||
return err;
|
||||
if (err < 0) {
|
||||
dev_err(i2c->dev, "failed to transfer message: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (msg.rx.ret != 0) {
|
||||
if (msg.rx.ret == -BPMP_EAGAIN) {
|
||||
dev_dbg(i2c->dev, "arbitration lost\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (msg.rx.ret == -BPMP_ETIMEDOUT) {
|
||||
dev_dbg(i2c->dev, "timeout\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (msg.rx.ret == -BPMP_ENXIO) {
|
||||
dev_dbg(i2c->dev, "NAK\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
dev_err(i2c->dev, "transaction failed: %d\n", msg.rx.ret);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_bpmp_i2c_xfer_common(struct i2c_adapter *adapter,
|
||||
@ -238,12 +240,7 @@ static int tegra_bpmp_i2c_xfer_common(struct i2c_adapter *adapter,
|
||||
memset(&request, 0, sizeof(request));
|
||||
memset(&response, 0, sizeof(response));
|
||||
|
||||
err = tegra_bpmp_serialize_i2c_msg(i2c, &request, msgs, num);
|
||||
if (err < 0) {
|
||||
dev_err(i2c->dev, "failed to serialize message: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
tegra_bpmp_serialize_i2c_msg(i2c, &request, msgs, num);
|
||||
err = tegra_bpmp_i2c_msg_xfer(i2c, &request, &response, atomic);
|
||||
if (err < 0) {
|
||||
dev_err(i2c->dev, "failed to transfer message: %d\n", err);
|
||||
|
@ -19,7 +19,6 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#define MAILBOX_OP_TIMEOUT 1000 /* Operation time out in ms */
|
||||
#define MAILBOX_I2C_INDEX 0
|
||||
|
@ -706,7 +706,7 @@ static int xiic_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
||||
dev_dbg(adap->dev.parent, "%s entry SR: 0x%x\n", __func__,
|
||||
xiic_getreg8(i2c, XIIC_SR_REG_OFFSET));
|
||||
|
||||
err = pm_runtime_get_sync(i2c->dev);
|
||||
err = pm_runtime_resume_and_get(i2c->dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@ -873,7 +873,7 @@ static int xiic_i2c_remove(struct platform_device *pdev)
|
||||
/* remove adapter & data */
|
||||
i2c_del_adapter(&i2c->adap);
|
||||
|
||||
ret = pm_runtime_get_sync(i2c->dev);
|
||||
ret = pm_runtime_resume_and_get(i2c->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -47,7 +47,6 @@ EXPORT_SYMBOL_GPL(__i2c_first_dynamic_bus_num);
|
||||
*
|
||||
* The board info passed can safely be __initdata, but be careful of embedded
|
||||
* pointers (for platform_data, functions, etc) since that won't be copied.
|
||||
* Device properties are deep-copied though.
|
||||
*/
|
||||
int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len)
|
||||
{
|
||||
@ -72,16 +71,6 @@ int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsig
|
||||
devinfo->busnum = busnum;
|
||||
devinfo->board_info = *info;
|
||||
|
||||
if (info->properties) {
|
||||
devinfo->board_info.properties =
|
||||
property_entries_dup(info->properties);
|
||||
if (IS_ERR(devinfo->board_info.properties)) {
|
||||
status = PTR_ERR(devinfo->board_info.properties);
|
||||
kfree(devinfo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (info->resources) {
|
||||
devinfo->board_info.resources =
|
||||
kmemdup(info->resources,
|
||||
|
@ -76,6 +76,27 @@ void i2c_transfer_trace_unreg(void)
|
||||
static_branch_dec(&i2c_trace_msg_key);
|
||||
}
|
||||
|
||||
const char *i2c_freq_mode_string(u32 bus_freq_hz)
|
||||
{
|
||||
switch (bus_freq_hz) {
|
||||
case I2C_MAX_STANDARD_MODE_FREQ:
|
||||
return "Standard Mode (100 kHz)";
|
||||
case I2C_MAX_FAST_MODE_FREQ:
|
||||
return "Fast Mode (400 kHz)";
|
||||
case I2C_MAX_FAST_MODE_PLUS_FREQ:
|
||||
return "Fast Mode Plus (1.0 MHz)";
|
||||
case I2C_MAX_TURBO_MODE_FREQ:
|
||||
return "Turbo Mode (1.4 MHz)";
|
||||
case I2C_MAX_HIGH_SPEED_MODE_FREQ:
|
||||
return "High Speed Mode (3.4 MHz)";
|
||||
case I2C_MAX_ULTRA_FAST_MODE_FREQ:
|
||||
return "Ultra Fast Mode (5.0 MHz)";
|
||||
default:
|
||||
return "Unknown Mode";
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_freq_mode_string);
|
||||
|
||||
const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
|
||||
const struct i2c_client *client)
|
||||
{
|
||||
@ -249,7 +270,7 @@ EXPORT_SYMBOL_GPL(i2c_generic_scl_recovery);
|
||||
int i2c_recover_bus(struct i2c_adapter *adap)
|
||||
{
|
||||
if (!adap->bus_recovery_info)
|
||||
return -EOPNOTSUPP;
|
||||
return -EBUSY;
|
||||
|
||||
dev_dbg(&adap->dev, "Trying i2c bus recovery\n");
|
||||
return adap->bus_recovery_info->recover_bus(adap);
|
||||
@ -519,6 +540,13 @@ static int i2c_device_probe(struct device *dev)
|
||||
if (status)
|
||||
goto err_clear_wakeup_irq;
|
||||
|
||||
client->devres_group_id = devres_open_group(&client->dev, NULL,
|
||||
GFP_KERNEL);
|
||||
if (!client->devres_group_id) {
|
||||
status = -ENOMEM;
|
||||
goto err_detach_pm_domain;
|
||||
}
|
||||
|
||||
/*
|
||||
* When there are no more users of probe(),
|
||||
* rename probe_new to probe.
|
||||
@ -531,11 +559,21 @@ static int i2c_device_probe(struct device *dev)
|
||||
else
|
||||
status = -EINVAL;
|
||||
|
||||
/*
|
||||
* Note that we are not closing the devres group opened above so
|
||||
* even resources that were attached to the device after probe is
|
||||
* run are released when i2c_device_remove() is executed. This is
|
||||
* needed as some drivers would allocate additional resources,
|
||||
* for example when updating firmware.
|
||||
*/
|
||||
|
||||
if (status)
|
||||
goto err_detach_pm_domain;
|
||||
goto err_release_driver_resources;
|
||||
|
||||
return 0;
|
||||
|
||||
err_release_driver_resources:
|
||||
devres_release_group(&client->dev, client->devres_group_id);
|
||||
err_detach_pm_domain:
|
||||
dev_pm_domain_detach(&client->dev, true);
|
||||
err_clear_wakeup_irq:
|
||||
@ -564,6 +602,8 @@ static int i2c_device_remove(struct device *dev)
|
||||
dev_warn(dev, "remove failed (%pe), will be ignored\n", ERR_PTR(status));
|
||||
}
|
||||
|
||||
devres_release_group(&client->dev, client->devres_group_id);
|
||||
|
||||
dev_pm_domain_detach(&client->dev, true);
|
||||
|
||||
dev_pm_clear_wake_irq(&client->dev);
|
||||
@ -612,7 +652,7 @@ modalias_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
if (len != -ENODEV)
|
||||
return len;
|
||||
|
||||
len = acpi_device_modalias(dev, buf, PAGE_SIZE -1);
|
||||
len = acpi_device_modalias(dev, buf, PAGE_SIZE - 1);
|
||||
if (len != -ENODEV)
|
||||
return len;
|
||||
|
||||
@ -910,11 +950,11 @@ i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *inf
|
||||
|
||||
i2c_dev_set_name(adap, client, info);
|
||||
|
||||
if (info->properties) {
|
||||
status = device_add_properties(&client->dev, info->properties);
|
||||
if (info->swnode) {
|
||||
status = device_add_software_node(&client->dev, info->swnode);
|
||||
if (status) {
|
||||
dev_err(&adap->dev,
|
||||
"Failed to add properties to client %s: %d\n",
|
||||
"Failed to add software node to client %s: %d\n",
|
||||
client->name, status);
|
||||
goto out_err_put_of_node;
|
||||
}
|
||||
@ -922,16 +962,15 @@ i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *inf
|
||||
|
||||
status = device_register(&client->dev);
|
||||
if (status)
|
||||
goto out_free_props;
|
||||
goto out_remove_swnode;
|
||||
|
||||
dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
|
||||
client->name, dev_name(&client->dev));
|
||||
|
||||
return client;
|
||||
|
||||
out_free_props:
|
||||
if (info->properties)
|
||||
device_remove_properties(&client->dev);
|
||||
out_remove_swnode:
|
||||
device_remove_software_node(&client->dev);
|
||||
out_err_put_of_node:
|
||||
of_node_put(info->of_node);
|
||||
out_err:
|
||||
@ -961,6 +1000,7 @@ void i2c_unregister_device(struct i2c_client *client)
|
||||
|
||||
if (ACPI_COMPANION(&client->dev))
|
||||
acpi_device_clear_enumerated(ACPI_COMPANION(&client->dev));
|
||||
device_remove_software_node(&client->dev);
|
||||
device_unregister(&client->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_unregister_device);
|
||||
@ -1017,15 +1057,9 @@ struct i2c_client *i2c_new_dummy_device(struct i2c_adapter *adapter, u16 address
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_new_dummy_device);
|
||||
|
||||
struct i2c_dummy_devres {
|
||||
struct i2c_client *client;
|
||||
};
|
||||
|
||||
static void devm_i2c_release_dummy(struct device *dev, void *res)
|
||||
static void devm_i2c_release_dummy(void *client)
|
||||
{
|
||||
struct i2c_dummy_devres *this = res;
|
||||
|
||||
i2c_unregister_device(this->client);
|
||||
i2c_unregister_device(client);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1042,20 +1076,16 @@ struct i2c_client *devm_i2c_new_dummy_device(struct device *dev,
|
||||
struct i2c_adapter *adapter,
|
||||
u16 address)
|
||||
{
|
||||
struct i2c_dummy_devres *dr;
|
||||
struct i2c_client *client;
|
||||
|
||||
dr = devres_alloc(devm_i2c_release_dummy, sizeof(*dr), GFP_KERNEL);
|
||||
if (!dr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
int ret;
|
||||
|
||||
client = i2c_new_dummy_device(adapter, address);
|
||||
if (IS_ERR(client)) {
|
||||
devres_free(dr);
|
||||
} else {
|
||||
dr->client = client;
|
||||
devres_add(dev, dr);
|
||||
}
|
||||
if (IS_ERR(client))
|
||||
return client;
|
||||
|
||||
ret = devm_add_action_or_reset(dev, devm_i2c_release_dummy, client);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return client;
|
||||
}
|
||||
@ -1704,6 +1734,32 @@ void i2c_del_adapter(struct i2c_adapter *adap)
|
||||
}
|
||||
EXPORT_SYMBOL(i2c_del_adapter);
|
||||
|
||||
static void devm_i2c_del_adapter(void *adapter)
|
||||
{
|
||||
i2c_del_adapter(adapter);
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_i2c_add_adapter - device-managed variant of i2c_add_adapter()
|
||||
* @dev: managing device for adding this I2C adapter
|
||||
* @adapter: the adapter to add
|
||||
* Context: can sleep
|
||||
*
|
||||
* Add adapter with dynamic bus number, same with i2c_add_adapter()
|
||||
* but the adapter will be auto deleted on driver detach.
|
||||
*/
|
||||
int devm_i2c_add_adapter(struct device *dev, struct i2c_adapter *adapter)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_add_adapter(adapter);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_add_action_or_reset(dev, devm_i2c_del_adapter, adapter);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_i2c_add_adapter);
|
||||
|
||||
static void i2c_parse_timing(struct device *dev, char *prop_name, u32 *cur_val_p,
|
||||
u32 def_val, bool use_def)
|
||||
{
|
||||
|
@ -440,8 +440,13 @@ static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
sizeof(rdwr_arg)))
|
||||
return -EFAULT;
|
||||
|
||||
/* Put an arbitrary limit on the number of messages that can
|
||||
* be sent at once */
|
||||
if (!rdwr_arg.msgs || rdwr_arg.nmsgs == 0)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Put an arbitrary limit on the number of messages that can
|
||||
* be sent at once
|
||||
*/
|
||||
if (rdwr_arg.nmsgs > I2C_RDWR_IOCTL_MAX_MSGS)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -1885,8 +1885,6 @@ static int elantech_create_smbus(struct psmouse *psmouse,
|
||||
};
|
||||
unsigned int idx = 0;
|
||||
|
||||
smbus_board.properties = i2c_props;
|
||||
|
||||
i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-size-x",
|
||||
info->x_max + 1);
|
||||
i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-size-y",
|
||||
@ -1918,6 +1916,10 @@ static int elantech_create_smbus(struct psmouse *psmouse,
|
||||
if (elantech_is_buttonpad(info))
|
||||
i2c_props[idx++] = PROPERTY_ENTRY_BOOL("elan,clickpad");
|
||||
|
||||
smbus_board.fwnode = fwnode_create_software_node(i2c_props, NULL);
|
||||
if (IS_ERR(smbus_board.fwnode))
|
||||
return PTR_ERR(smbus_board.fwnode);
|
||||
|
||||
return psmouse_smbus_init(psmouse, &smbus_board, NULL, 0, false,
|
||||
leave_breadcrumbs);
|
||||
}
|
||||
|
@ -52,12 +52,15 @@ struct i2c_peripheral {
|
||||
enum i2c_adapter_type type;
|
||||
u32 pci_devid;
|
||||
|
||||
const struct property_entry *properties;
|
||||
|
||||
struct i2c_client *client;
|
||||
};
|
||||
|
||||
struct acpi_peripheral {
|
||||
char hid[ACPI_ID_LEN];
|
||||
const struct property_entry *properties;
|
||||
struct software_node swnode;
|
||||
struct i2c_client *client;
|
||||
};
|
||||
|
||||
struct chromeos_laptop {
|
||||
@ -68,7 +71,7 @@ struct chromeos_laptop {
|
||||
struct i2c_peripheral *i2c_peripherals;
|
||||
unsigned int num_i2c_peripherals;
|
||||
|
||||
const struct acpi_peripheral *acpi_peripherals;
|
||||
struct acpi_peripheral *acpi_peripherals;
|
||||
unsigned int num_acpi_peripherals;
|
||||
};
|
||||
|
||||
@ -161,7 +164,7 @@ static void chromeos_laptop_check_adapter(struct i2c_adapter *adapter)
|
||||
|
||||
static bool chromeos_laptop_adjust_client(struct i2c_client *client)
|
||||
{
|
||||
const struct acpi_peripheral *acpi_dev;
|
||||
struct acpi_peripheral *acpi_dev;
|
||||
struct acpi_device_id acpi_ids[2] = { };
|
||||
int i;
|
||||
int error;
|
||||
@ -175,8 +178,7 @@ static bool chromeos_laptop_adjust_client(struct i2c_client *client)
|
||||
memcpy(acpi_ids[0].id, acpi_dev->hid, ACPI_ID_LEN);
|
||||
|
||||
if (acpi_match_device(acpi_ids, &client->dev)) {
|
||||
error = device_add_properties(&client->dev,
|
||||
acpi_dev->properties);
|
||||
error = device_add_software_node(&client->dev, &acpi_dev->swnode);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
"failed to add properties: %d\n",
|
||||
@ -184,6 +186,8 @@ static bool chromeos_laptop_adjust_client(struct i2c_client *client)
|
||||
break;
|
||||
}
|
||||
|
||||
acpi_dev->client = client;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -193,15 +197,28 @@ static bool chromeos_laptop_adjust_client(struct i2c_client *client)
|
||||
|
||||
static void chromeos_laptop_detach_i2c_client(struct i2c_client *client)
|
||||
{
|
||||
struct acpi_peripheral *acpi_dev;
|
||||
struct i2c_peripheral *i2c_dev;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) {
|
||||
i2c_dev = &cros_laptop->i2c_peripherals[i];
|
||||
if (has_acpi_companion(&client->dev))
|
||||
for (i = 0; i < cros_laptop->num_acpi_peripherals; i++) {
|
||||
acpi_dev = &cros_laptop->acpi_peripherals[i];
|
||||
|
||||
if (i2c_dev->client == client)
|
||||
i2c_dev->client = NULL;
|
||||
}
|
||||
if (acpi_dev->client == client) {
|
||||
acpi_dev->client = NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) {
|
||||
i2c_dev = &cros_laptop->i2c_peripherals[i];
|
||||
|
||||
if (i2c_dev->client == client) {
|
||||
i2c_dev->client = NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int chromeos_laptop_i2c_notifier_call(struct notifier_block *nb,
|
||||
@ -302,28 +319,26 @@ static struct i2c_peripheral chromebook_pixel_peripherals[] __initdata = {
|
||||
.board_info = {
|
||||
I2C_BOARD_INFO("atmel_mxt_ts",
|
||||
ATMEL_TS_I2C_ADDR),
|
||||
.properties =
|
||||
chromebook_atmel_touchscreen_props,
|
||||
.flags = I2C_CLIENT_WAKE,
|
||||
},
|
||||
.dmi_name = "touchscreen",
|
||||
.irqflags = IRQF_TRIGGER_FALLING,
|
||||
.type = I2C_ADAPTER_PANEL,
|
||||
.alt_addr = ATMEL_TS_I2C_BL_ADDR,
|
||||
.properties = chromebook_atmel_touchscreen_props,
|
||||
},
|
||||
/* Touchpad. */
|
||||
{
|
||||
.board_info = {
|
||||
I2C_BOARD_INFO("atmel_mxt_tp",
|
||||
ATMEL_TP_I2C_ADDR),
|
||||
.properties =
|
||||
chromebook_pixel_trackpad_props,
|
||||
.flags = I2C_CLIENT_WAKE,
|
||||
},
|
||||
.dmi_name = "trackpad",
|
||||
.irqflags = IRQF_TRIGGER_FALLING,
|
||||
.type = I2C_ADAPTER_VGADDC,
|
||||
.alt_addr = ATMEL_TP_I2C_BL_ADDR,
|
||||
.properties = chromebook_pixel_trackpad_props,
|
||||
},
|
||||
/* Light Sensor. */
|
||||
{
|
||||
@ -414,8 +429,6 @@ static struct i2c_peripheral acer_c720_peripherals[] __initdata = {
|
||||
.board_info = {
|
||||
I2C_BOARD_INFO("atmel_mxt_ts",
|
||||
ATMEL_TS_I2C_ADDR),
|
||||
.properties =
|
||||
chromebook_atmel_touchscreen_props,
|
||||
.flags = I2C_CLIENT_WAKE,
|
||||
},
|
||||
.dmi_name = "touchscreen",
|
||||
@ -423,6 +436,7 @@ static struct i2c_peripheral acer_c720_peripherals[] __initdata = {
|
||||
.type = I2C_ADAPTER_DESIGNWARE,
|
||||
.pci_devid = PCI_DEVID(0, PCI_DEVFN(0x15, 0x2)),
|
||||
.alt_addr = ATMEL_TS_I2C_BL_ADDR,
|
||||
.properties = chromebook_atmel_touchscreen_props,
|
||||
},
|
||||
/* Touchpad. */
|
||||
{
|
||||
@ -498,12 +512,16 @@ static struct acpi_peripheral samus_peripherals[] __initdata = {
|
||||
/* Touchpad */
|
||||
{
|
||||
.hid = "ATML0000",
|
||||
.properties = samus_trackpad_props,
|
||||
.swnode = {
|
||||
.properties = samus_trackpad_props,
|
||||
},
|
||||
},
|
||||
/* Touchsceen */
|
||||
{
|
||||
.hid = "ATML0001",
|
||||
.properties = chromebook_atmel_touchscreen_props,
|
||||
.swnode = {
|
||||
.properties = chromebook_atmel_touchscreen_props,
|
||||
},
|
||||
},
|
||||
};
|
||||
DECLARE_ACPI_CROS_LAPTOP(samus);
|
||||
@ -512,12 +530,16 @@ static struct acpi_peripheral generic_atmel_peripherals[] __initdata = {
|
||||
/* Touchpad */
|
||||
{
|
||||
.hid = "ATML0000",
|
||||
.properties = chromebook_pixel_trackpad_props,
|
||||
.swnode = {
|
||||
.properties = chromebook_pixel_trackpad_props,
|
||||
},
|
||||
},
|
||||
/* Touchsceen */
|
||||
{
|
||||
.hid = "ATML0001",
|
||||
.properties = chromebook_atmel_touchscreen_props,
|
||||
.swnode = {
|
||||
.properties = chromebook_atmel_touchscreen_props,
|
||||
},
|
||||
},
|
||||
};
|
||||
DECLARE_ACPI_CROS_LAPTOP(generic_atmel);
|
||||
@ -743,12 +765,11 @@ chromeos_laptop_prepare_i2c_peripherals(struct chromeos_laptop *cros_laptop,
|
||||
if (error)
|
||||
goto err_out;
|
||||
|
||||
/* We need to deep-copy properties */
|
||||
if (info->properties) {
|
||||
info->properties =
|
||||
property_entries_dup(info->properties);
|
||||
if (IS_ERR(info->properties)) {
|
||||
error = PTR_ERR(info->properties);
|
||||
/* Create primary fwnode for the device - copies everything */
|
||||
if (i2c_dev->properties) {
|
||||
info->fwnode = fwnode_create_software_node(i2c_dev->properties, NULL);
|
||||
if (IS_ERR(info->fwnode)) {
|
||||
error = PTR_ERR(info->fwnode);
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
@ -760,8 +781,8 @@ err_out:
|
||||
while (--i >= 0) {
|
||||
i2c_dev = &cros_laptop->i2c_peripherals[i];
|
||||
info = &i2c_dev->board_info;
|
||||
if (info->properties)
|
||||
property_entries_free(info->properties);
|
||||
if (!IS_ERR_OR_NULL(info->fwnode))
|
||||
fwnode_remove_software_node(info->fwnode);
|
||||
}
|
||||
kfree(cros_laptop->i2c_peripherals);
|
||||
return error;
|
||||
@ -801,11 +822,11 @@ chromeos_laptop_prepare_acpi_peripherals(struct chromeos_laptop *cros_laptop,
|
||||
*acpi_dev = *src_dev;
|
||||
|
||||
/* We need to deep-copy properties */
|
||||
if (src_dev->properties) {
|
||||
acpi_dev->properties =
|
||||
property_entries_dup(src_dev->properties);
|
||||
if (IS_ERR(acpi_dev->properties)) {
|
||||
error = PTR_ERR(acpi_dev->properties);
|
||||
if (src_dev->swnode.properties) {
|
||||
acpi_dev->swnode.properties =
|
||||
property_entries_dup(src_dev->swnode.properties);
|
||||
if (IS_ERR(acpi_dev->swnode.properties)) {
|
||||
error = PTR_ERR(acpi_dev->swnode.properties);
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
@ -821,8 +842,8 @@ chromeos_laptop_prepare_acpi_peripherals(struct chromeos_laptop *cros_laptop,
|
||||
err_out:
|
||||
while (--i >= 0) {
|
||||
acpi_dev = &acpi_peripherals[i];
|
||||
if (acpi_dev->properties)
|
||||
property_entries_free(acpi_dev->properties);
|
||||
if (!IS_ERR_OR_NULL(acpi_dev->swnode.properties))
|
||||
property_entries_free(acpi_dev->swnode.properties);
|
||||
}
|
||||
|
||||
kfree(acpi_peripherals);
|
||||
@ -833,21 +854,20 @@ static void chromeos_laptop_destroy(const struct chromeos_laptop *cros_laptop)
|
||||
{
|
||||
const struct acpi_peripheral *acpi_dev;
|
||||
struct i2c_peripheral *i2c_dev;
|
||||
struct i2c_board_info *info;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cros_laptop->num_i2c_peripherals; i++) {
|
||||
i2c_dev = &cros_laptop->i2c_peripherals[i];
|
||||
info = &i2c_dev->board_info;
|
||||
|
||||
i2c_unregister_device(i2c_dev->client);
|
||||
property_entries_free(info->properties);
|
||||
}
|
||||
|
||||
for (i = 0; i < cros_laptop->num_acpi_peripherals; i++) {
|
||||
acpi_dev = &cros_laptop->acpi_peripherals[i];
|
||||
|
||||
property_entries_free(acpi_dev->properties);
|
||||
if (acpi_dev->client)
|
||||
device_remove_software_node(&acpi_dev->client->dev);
|
||||
|
||||
property_entries_free(acpi_dev->swnode.properties);
|
||||
}
|
||||
|
||||
kfree(cros_laptop->i2c_peripherals);
|
||||
|
@ -35,6 +35,10 @@ static const struct property_entry bq27xxx_props[] = {
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct software_node bq27xxx_node = {
|
||||
.properties = bq27xxx_props,
|
||||
};
|
||||
|
||||
int cht_int33fe_microb_probe(struct cht_int33fe_data *data)
|
||||
{
|
||||
struct device *dev = data->dev;
|
||||
@ -43,7 +47,7 @@ int cht_int33fe_microb_probe(struct cht_int33fe_data *data)
|
||||
memset(&board_info, 0, sizeof(board_info));
|
||||
strscpy(board_info.type, "bq27542", ARRAY_SIZE(board_info.type));
|
||||
board_info.dev_name = "bq27542";
|
||||
board_info.properties = bq27xxx_props;
|
||||
board_info.swnode = &bq27xxx_node;
|
||||
data->battery_fg = i2c_acpi_new_device(dev, 1, &board_info);
|
||||
|
||||
return PTR_ERR_OR_ZERO(data->battery_fg);
|
||||
|
@ -51,6 +51,9 @@ struct module;
|
||||
struct property_entry;
|
||||
|
||||
#if IS_ENABLED(CONFIG_I2C)
|
||||
/* Return the Frequency mode string based on the bus frequency */
|
||||
const char *i2c_freq_mode_string(u32 bus_freq_hz);
|
||||
|
||||
/*
|
||||
* The master routines are the ones normally used to transmit data to devices
|
||||
* on a bus (or read from them). Apart from two basic transfer functions to
|
||||
@ -306,6 +309,8 @@ struct i2c_driver {
|
||||
* userspace_devices list
|
||||
* @slave_cb: Callback when I2C slave mode of an adapter is used. The adapter
|
||||
* calls it to pass on slave events to the slave driver.
|
||||
* @devres_group_id: id of the devres group that will be created for resources
|
||||
* acquired when probing this device.
|
||||
*
|
||||
* An i2c_client identifies a single device (i.e. chip) connected to an
|
||||
* i2c bus. The behaviour exposed to Linux is defined by the driver
|
||||
@ -334,6 +339,7 @@ struct i2c_client {
|
||||
#if IS_ENABLED(CONFIG_I2C_SLAVE)
|
||||
i2c_slave_cb_t slave_cb; /* callback for slave mode */
|
||||
#endif
|
||||
void *devres_group_id; /* ID of probe devres group */
|
||||
};
|
||||
#define to_i2c_client(d) container_of(d, struct i2c_client, dev)
|
||||
|
||||
@ -391,7 +397,7 @@ static inline bool i2c_detect_slave_mode(struct device *dev) { return false; }
|
||||
* @platform_data: stored in i2c_client.dev.platform_data
|
||||
* @of_node: pointer to OpenFirmware device node
|
||||
* @fwnode: device node supplied by the platform firmware
|
||||
* @properties: additional device properties for the device
|
||||
* @swnode: software node for the device
|
||||
* @resources: resources associated with the device
|
||||
* @num_resources: number of resources in the @resources array
|
||||
* @irq: stored in i2c_client.irq
|
||||
@ -415,7 +421,7 @@ struct i2c_board_info {
|
||||
void *platform_data;
|
||||
struct device_node *of_node;
|
||||
struct fwnode_handle *fwnode;
|
||||
const struct property_entry *properties;
|
||||
const struct software_node *swnode;
|
||||
const struct resource *resources;
|
||||
unsigned int num_resources;
|
||||
int irq;
|
||||
@ -687,6 +693,8 @@ struct i2c_adapter_quirks {
|
||||
#define I2C_AQ_NO_ZERO_LEN_READ BIT(5)
|
||||
#define I2C_AQ_NO_ZERO_LEN_WRITE BIT(6)
|
||||
#define I2C_AQ_NO_ZERO_LEN (I2C_AQ_NO_ZERO_LEN_READ | I2C_AQ_NO_ZERO_LEN_WRITE)
|
||||
/* adapter cannot do repeated START */
|
||||
#define I2C_AQ_NO_REP_START BIT(7)
|
||||
|
||||
/*
|
||||
* i2c_adapter is the structure used to identify a physical i2c bus along
|
||||
@ -844,6 +852,7 @@ static inline void i2c_mark_adapter_resumed(struct i2c_adapter *adap)
|
||||
*/
|
||||
#if IS_ENABLED(CONFIG_I2C)
|
||||
int i2c_add_adapter(struct i2c_adapter *adap);
|
||||
int devm_i2c_add_adapter(struct device *dev, struct i2c_adapter *adapter);
|
||||
void i2c_del_adapter(struct i2c_adapter *adap);
|
||||
int i2c_add_numbered_adapter(struct i2c_adapter *adap);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user