mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00
Merge branch 'i2c/for-4.5' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang: "Quite some driver updates: - piix4 can now handle multiplexed adapters - brcmstb, xlr, eg20t, designware drivers support more SoCs - emev2 gained i2c slave support - img-scb and rcar got bigger refactoring to remove issues - lots of common driver updates i2c core changes: - new quirk flag when an adapter does not support clock stretching, so clients can be configured to avoid that if possible - added a helper function to retrieve timing parameters from firmware (with rcar being the first user) - "multi-master" DT binding added so drivers can adapt to this setting (like disabling PM to keep arbitration working) - RuntimePM for the logical adapter device is now always enabled by the core to ensure propagation from childs to the parent (the HW device) - new macro builtin_i2c_driver to reduce boilerplate" * 'i2c/for-4.5' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (70 commits) i2c: create builtin_i2c_driver to avoid registration boilerplate i2c: imx: fix i2c resource leak with dma transfer dt-bindings: i2c: eeprom: add another EEPROM device dt-bindings: move I2C eeprom descriptions to the proper file i2c: designware: Do not require clock when SSCN and FFCN are provided DT: i2c: trivial-devices: Add Epson RX8010 and MPL3115 i2c: s3c2410: remove superfluous runtime PM calls i2c: always enable RuntimePM for the adapter device i2c: designware: retry transfer on transient failure i2c: ibm_iic: rename i2c_timings struct due to clash with generic version i2c: designware: Add support for AMD Seattle I2C i2c: imx: Remove unneeded comments i2c: st: use to_platform_device() i2c: designware: use to_pci_dev() i2c: brcmstb: Adding support for CM and DSL SoCs i2c: mediatek: fix i2c multi transfer issue in high speed mode i2c: imx: improve code readability i2c: imx: Improve message log when DMA is not used i2c: imx: add runtime pm support to improve the performance i2c: imx: init bus recovery info before adding i2c adapter ...
This commit is contained in:
commit
32250e4a5f
32 changed files with 1253 additions and 419 deletions
|
@ -2,11 +2,22 @@ EEPROMs (I2C)
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
|
|
||||||
- compatible : should be "<manufacturer>,<type>"
|
- compatible : should be "<manufacturer>,<type>", like these:
|
||||||
If there is no specific driver for <manufacturer>, a generic
|
|
||||||
driver based on <type> is selected. Possible types are:
|
"atmel,24c00", "atmel,24c01", "atmel,24c02", "atmel,24c04",
|
||||||
24c00, 24c01, 24c02, 24c04, 24c08, 24c16, 24c32, 24c64,
|
"atmel,24c08", "atmel,24c16", "atmel,24c32", "atmel,24c64",
|
||||||
24c128, 24c256, 24c512, 24c1024, spd
|
"atmel,24c128", "atmel,24c256", "atmel,24c512", "atmel,24c1024"
|
||||||
|
|
||||||
|
"catalyst,24c32"
|
||||||
|
|
||||||
|
"ramtron,24c64"
|
||||||
|
|
||||||
|
"renesas,r1ex24002"
|
||||||
|
|
||||||
|
If there is no specific driver for <manufacturer>, a generic
|
||||||
|
driver based on <type> is selected. Possible types are:
|
||||||
|
"24c00", "24c01", "24c02", "24c04", "24c08", "24c16", "24c32", "24c64",
|
||||||
|
"24c128", "24c256", "24c512", "24c1024", "spd"
|
||||||
|
|
||||||
- reg : the I2C address of the EEPROM
|
- reg : the I2C address of the EEPROM
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ I2C for Atmel platforms
|
||||||
Required properties :
|
Required properties :
|
||||||
- compatible : Must be "atmel,at91rm9200-i2c", "atmel,at91sam9261-i2c",
|
- compatible : Must be "atmel,at91rm9200-i2c", "atmel,at91sam9261-i2c",
|
||||||
"atmel,at91sam9260-i2c", "atmel,at91sam9g20-i2c", "atmel,at91sam9g10-i2c",
|
"atmel,at91sam9260-i2c", "atmel,at91sam9g20-i2c", "atmel,at91sam9g10-i2c",
|
||||||
"atmel,at91sam9x5-i2c" or "atmel,sama5d2-i2c"
|
"atmel,at91sam9x5-i2c", "atmel,sama5d4-i2c" or "atmel,sama5d2-i2c"
|
||||||
- reg: physical base address of the controller and length of memory mapped
|
- reg: physical base address of the controller and length of memory mapped
|
||||||
region.
|
region.
|
||||||
- interrupts: interrupt number to the cpu.
|
- interrupts: interrupt number to the cpu.
|
||||||
|
@ -17,6 +17,8 @@ Optional properties:
|
||||||
- dma-names: should contain "tx" and "rx".
|
- dma-names: should contain "tx" and "rx".
|
||||||
- atmel,fifo-size: maximum number of data the RX and TX FIFOs can store for FIFO
|
- atmel,fifo-size: maximum number of data the RX and TX FIFOs can store for FIFO
|
||||||
capable I2C controllers.
|
capable I2C controllers.
|
||||||
|
- i2c-sda-hold-time-ns: TWD hold time, only available for "atmel,sama5d4-i2c"
|
||||||
|
and "atmel,sama5d2-i2c".
|
||||||
- Child nodes conforming to i2c bus binding
|
- Child nodes conforming to i2c bus binding
|
||||||
|
|
||||||
Examples :
|
Examples :
|
||||||
|
@ -52,6 +54,7 @@ i2c0: i2c@f8034600 {
|
||||||
#size-cells = <0>;
|
#size-cells = <0>;
|
||||||
clocks = <&flx0>;
|
clocks = <&flx0>;
|
||||||
atmel,fifo-size = <16>;
|
atmel,fifo-size = <16>;
|
||||||
|
i2c-sda-hold-time-ns = <336>;
|
||||||
|
|
||||||
wm8731: wm8731@1a {
|
wm8731: wm8731@1a {
|
||||||
compatible = "wm8731";
|
compatible = "wm8731";
|
||||||
|
|
|
@ -2,7 +2,7 @@ Broadcom stb bsc iic master controller
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
|
|
||||||
- compatible: should be "brcm,brcmstb-i2c"
|
- compatible: should be "brcm,brcmstb-i2c" or "brcm,brcmper-i2c"
|
||||||
- clock-frequency: 32-bit decimal value of iic master clock freqency in Hz
|
- clock-frequency: 32-bit decimal value of iic master clock freqency in Hz
|
||||||
valid values are 375000, 390000, 187500, 200000
|
valid values are 375000, 390000, 187500, 200000
|
||||||
93750, 97500, 46875 and 50000
|
93750, 97500, 46875 and 50000
|
||||||
|
|
|
@ -20,6 +20,10 @@ Optional properties:
|
||||||
propoerty indicates the default frequency 100 kHz.
|
propoerty indicates the default frequency 100 kHz.
|
||||||
- clocks: clock specifier.
|
- clocks: clock specifier.
|
||||||
|
|
||||||
|
- i2c-scl-falling-time-ns: see i2c.txt
|
||||||
|
- i2c-scl-internal-delay-ns: see i2c.txt
|
||||||
|
- i2c-scl-rising-time-ns: see i2c.txt
|
||||||
|
|
||||||
Examples :
|
Examples :
|
||||||
|
|
||||||
i2c0: i2c@e6508000 {
|
i2c0: i2c@e6508000 {
|
||||||
|
|
|
@ -29,12 +29,38 @@ Optional properties
|
||||||
These properties may not be supported by all drivers. However, if a driver
|
These properties may not be supported by all drivers. However, if a driver
|
||||||
wants to support one of the below features, it should adapt the bindings below.
|
wants to support one of the below features, it should adapt the bindings below.
|
||||||
|
|
||||||
- clock-frequency - frequency of bus clock in Hz.
|
- clock-frequency
|
||||||
- wakeup-source - device can be used as a wakeup source.
|
frequency of bus clock in Hz.
|
||||||
|
|
||||||
- interrupts - interrupts used by the device.
|
- i2c-scl-falling-time-ns
|
||||||
- interrupt-names - "irq" and "wakeup" names are recognized by I2C core,
|
Number of nanoseconds the SCL signal takes to fall; t(f) in the I2C
|
||||||
other names are left to individual drivers.
|
specification.
|
||||||
|
|
||||||
|
- i2c-scl-internal-delay-ns
|
||||||
|
Number of nanoseconds the IP core additionally needs to setup SCL.
|
||||||
|
|
||||||
|
- i2c-scl-rising-time-ns
|
||||||
|
Number of nanoseconds the SCL signal takes to rise; t(r) in the I2C
|
||||||
|
specification.
|
||||||
|
|
||||||
|
- i2c-sda-falling-time-ns
|
||||||
|
Number of nanoseconds the SDA signal takes to fall; t(f) in the I2C
|
||||||
|
specification.
|
||||||
|
|
||||||
|
- interrupts
|
||||||
|
interrupts used by the device.
|
||||||
|
|
||||||
|
- interrupt-names
|
||||||
|
"irq" and "wakeup" names are recognized by I2C core, other names are
|
||||||
|
left to individual drivers.
|
||||||
|
|
||||||
|
- multi-master
|
||||||
|
states that there is another master active on this bus. The OS can use
|
||||||
|
this information to adapt power management to keep the arbitration awake
|
||||||
|
all the time, for example.
|
||||||
|
|
||||||
|
- wakeup-source
|
||||||
|
device can be used as a wakeup source.
|
||||||
|
|
||||||
Binding may contain optional "interrupts" property, describing interrupts
|
Binding may contain optional "interrupts" property, describing interrupts
|
||||||
used by the device. I2C core will assign "irq" interrupt (or the very first
|
used by the device. I2C core will assign "irq" interrupt (or the very first
|
||||||
|
|
|
@ -22,21 +22,9 @@ adi,adxl345 Three-Axis Digital Accelerometer
|
||||||
adi,adxl346 Three-Axis Digital Accelerometer (backward-compatibility value "adi,adxl345" must be listed too)
|
adi,adxl346 Three-Axis Digital Accelerometer (backward-compatibility value "adi,adxl345" must be listed too)
|
||||||
ams,iaq-core AMS iAQ-Core VOC Sensor
|
ams,iaq-core AMS iAQ-Core VOC Sensor
|
||||||
at,24c08 i2c serial eeprom (24cxx)
|
at,24c08 i2c serial eeprom (24cxx)
|
||||||
atmel,24c00 i2c serial eeprom (24cxx)
|
|
||||||
atmel,24c01 i2c serial eeprom (24cxx)
|
|
||||||
atmel,24c02 i2c serial eeprom (24cxx)
|
|
||||||
atmel,24c04 i2c serial eeprom (24cxx)
|
|
||||||
atmel,24c16 i2c serial eeprom (24cxx)
|
|
||||||
atmel,24c32 i2c serial eeprom (24cxx)
|
|
||||||
atmel,24c64 i2c serial eeprom (24cxx)
|
|
||||||
atmel,24c128 i2c serial eeprom (24cxx)
|
|
||||||
atmel,24c256 i2c serial eeprom (24cxx)
|
|
||||||
atmel,24c512 i2c serial eeprom (24cxx)
|
|
||||||
atmel,24c1024 i2c serial eeprom (24cxx)
|
|
||||||
atmel,at97sc3204t i2c trusted platform module (TPM)
|
atmel,at97sc3204t i2c trusted platform module (TPM)
|
||||||
capella,cm32181 CM32181: Ambient Light Sensor
|
capella,cm32181 CM32181: Ambient Light Sensor
|
||||||
capella,cm3232 CM3232: Ambient Light Sensor
|
capella,cm3232 CM3232: Ambient Light Sensor
|
||||||
catalyst,24c32 i2c serial eeprom
|
|
||||||
cirrus,cs42l51 Cirrus Logic CS42L51 audio codec
|
cirrus,cs42l51 Cirrus Logic CS42L51 audio codec
|
||||||
dallas,ds1307 64 x 8, Serial, I2C Real-Time Clock
|
dallas,ds1307 64 x 8, Serial, I2C Real-Time Clock
|
||||||
dallas,ds1338 I2C RTC with 56-Byte NV RAM
|
dallas,ds1338 I2C RTC with 56-Byte NV RAM
|
||||||
|
@ -50,11 +38,13 @@ dallas,ds4510 CPU Supervisor with Nonvolatile Memory and Programmable I/O
|
||||||
dallas,ds75 Digital Thermometer and Thermostat
|
dallas,ds75 Digital Thermometer and Thermostat
|
||||||
dlg,da9053 DA9053: flexible system level PMIC with multicore support
|
dlg,da9053 DA9053: flexible system level PMIC with multicore support
|
||||||
dlg,da9063 DA9063: system PMIC for quad-core application processors
|
dlg,da9063 DA9063: system PMIC for quad-core application processors
|
||||||
|
epson,rx8010 I2C-BUS INTERFACE REAL TIME CLOCK MODULE
|
||||||
epson,rx8025 High-Stability. I2C-Bus INTERFACE REAL TIME CLOCK MODULE
|
epson,rx8025 High-Stability. I2C-Bus INTERFACE REAL TIME CLOCK MODULE
|
||||||
epson,rx8581 I2C-BUS INTERFACE REAL TIME CLOCK MODULE
|
epson,rx8581 I2C-BUS INTERFACE REAL TIME CLOCK MODULE
|
||||||
fsl,mag3110 MAG3110: Xtrinsic High Accuracy, 3D Magnetometer
|
fsl,mag3110 MAG3110: Xtrinsic High Accuracy, 3D Magnetometer
|
||||||
fsl,mc13892 MC13892: Power Management Integrated Circuit (PMIC) for i.MX35/51
|
fsl,mc13892 MC13892: Power Management Integrated Circuit (PMIC) for i.MX35/51
|
||||||
fsl,mma8450 MMA8450Q: Xtrinsic Low-power, 3-axis Xtrinsic Accelerometer
|
fsl,mma8450 MMA8450Q: Xtrinsic Low-power, 3-axis Xtrinsic Accelerometer
|
||||||
|
fsl,mpl3115 MPL3115: Absolute Digital Pressure Sensor
|
||||||
fsl,mpr121 MPR121: Proximity Capacitive Touch Sensor Controller
|
fsl,mpr121 MPR121: Proximity Capacitive Touch Sensor Controller
|
||||||
fsl,sgtl5000 SGTL5000: Ultra Low-Power Audio Codec
|
fsl,sgtl5000 SGTL5000: Ultra Low-Power Audio Codec
|
||||||
gmt,g751 G751: Digital Temperature Sensor and Thermal Watchdog with Two-Wire Interface
|
gmt,g751 G751: Digital Temperature Sensor and Thermal Watchdog with Two-Wire Interface
|
||||||
|
@ -81,7 +71,6 @@ ovti,ov5642 OV5642: Color CMOS QSXGA (5-megapixel) Image Sensor with OmniBSI an
|
||||||
pericom,pt7c4338 Real-time Clock Module
|
pericom,pt7c4338 Real-time Clock Module
|
||||||
plx,pex8648 48-Lane, 12-Port PCI Express Gen 2 (5.0 GT/s) Switch
|
plx,pex8648 48-Lane, 12-Port PCI Express Gen 2 (5.0 GT/s) Switch
|
||||||
pulsedlight,lidar-lite-v2 Pulsedlight LIDAR range-finding sensor
|
pulsedlight,lidar-lite-v2 Pulsedlight LIDAR range-finding sensor
|
||||||
ramtron,24c64 i2c serial eeprom (24cxx)
|
|
||||||
ricoh,r2025sd I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
|
ricoh,r2025sd I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
|
||||||
ricoh,r2221tl I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
|
ricoh,r2221tl I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
|
||||||
ricoh,rs5c372a I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
|
ricoh,rs5c372a I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
|
||||||
|
|
|
@ -617,6 +617,10 @@ const struct i2c_algorithm i2c_bit_algo = {
|
||||||
};
|
};
|
||||||
EXPORT_SYMBOL(i2c_bit_algo);
|
EXPORT_SYMBOL(i2c_bit_algo);
|
||||||
|
|
||||||
|
const struct i2c_adapter_quirks i2c_bit_quirk_no_clk_stretch = {
|
||||||
|
.flags = I2C_AQ_NO_CLK_STRETCH,
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* registering functions to load algorithms at runtime
|
* registering functions to load algorithms at runtime
|
||||||
*/
|
*/
|
||||||
|
@ -635,6 +639,8 @@ static int __i2c_bit_add_bus(struct i2c_adapter *adap,
|
||||||
/* register new adapter to i2c module... */
|
/* register new adapter to i2c module... */
|
||||||
adap->algo = &i2c_bit_algo;
|
adap->algo = &i2c_bit_algo;
|
||||||
adap->retries = 3;
|
adap->retries = 3;
|
||||||
|
if (bit_adap->getscl == NULL)
|
||||||
|
adap->quirks = &i2c_bit_quirk_no_clk_stretch;
|
||||||
|
|
||||||
ret = add_adapter(adap);
|
ret = add_adapter(adap);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
|
|
@ -516,7 +516,7 @@ config I2C_EFM32
|
||||||
|
|
||||||
config I2C_EG20T
|
config I2C_EG20T
|
||||||
tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) I2C"
|
tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) I2C"
|
||||||
depends on PCI && (X86_32 || COMPILE_TEST)
|
depends on PCI && (X86_32 || MIPS || COMPILE_TEST)
|
||||||
help
|
help
|
||||||
This driver is for PCH(Platform controller Hub) I2C of EG20T which
|
This driver is for PCH(Platform controller Hub) I2C of EG20T which
|
||||||
is an IOH(Input/Output Hub) for x86 embedded processor.
|
is an IOH(Input/Output Hub) for x86 embedded processor.
|
||||||
|
@ -532,6 +532,7 @@ config I2C_EG20T
|
||||||
config I2C_EMEV2
|
config I2C_EMEV2
|
||||||
tristate "EMMA Mobile series I2C adapter"
|
tristate "EMMA Mobile series I2C adapter"
|
||||||
depends on HAVE_CLK
|
depends on HAVE_CLK
|
||||||
|
select I2C_SLAVE
|
||||||
help
|
help
|
||||||
If you say yes to this option, support will be included for the
|
If you say yes to this option, support will be included for the
|
||||||
I2C interface on the Renesas Electronics EM/EV family of processors.
|
I2C interface on the Renesas Electronics EM/EV family of processors.
|
||||||
|
@ -963,11 +964,11 @@ config I2C_XILINX
|
||||||
will be called xilinx_i2c.
|
will be called xilinx_i2c.
|
||||||
|
|
||||||
config I2C_XLR
|
config I2C_XLR
|
||||||
tristate "XLR I2C support"
|
tristate "Netlogic XLR and Sigma Designs I2C support"
|
||||||
depends on CPU_XLR
|
depends on CPU_XLR || ARCH_TANGOX
|
||||||
help
|
help
|
||||||
This driver enables support for the on-chip I2C interface of
|
This driver enables support for the on-chip I2C interface of
|
||||||
the Netlogic XLR/XLS MIPS processors.
|
the Netlogic XLR/XLS MIPS processors and Sigma Designs SOCs.
|
||||||
|
|
||||||
This driver can also be built as a module. If so, the module
|
This driver can also be built as a module. If so, the module
|
||||||
will be called i2c-xlr.
|
will be called i2c-xlr.
|
||||||
|
|
|
@ -64,6 +64,8 @@
|
||||||
#define AT91_TWI_IADR 0x000c /* Internal Address Register */
|
#define AT91_TWI_IADR 0x000c /* Internal Address Register */
|
||||||
|
|
||||||
#define AT91_TWI_CWGR 0x0010 /* Clock Waveform Generator Reg */
|
#define AT91_TWI_CWGR 0x0010 /* Clock Waveform Generator Reg */
|
||||||
|
#define AT91_TWI_CWGR_HOLD_MAX 0x1f
|
||||||
|
#define AT91_TWI_CWGR_HOLD(x) (((x) & AT91_TWI_CWGR_HOLD_MAX) << 24)
|
||||||
|
|
||||||
#define AT91_TWI_SR 0x0020 /* Status Register */
|
#define AT91_TWI_SR 0x0020 /* Status Register */
|
||||||
#define AT91_TWI_TXCOMP BIT(0) /* Transmission Complete */
|
#define AT91_TWI_TXCOMP BIT(0) /* Transmission Complete */
|
||||||
|
@ -110,6 +112,7 @@ struct at91_twi_pdata {
|
||||||
unsigned clk_offset;
|
unsigned clk_offset;
|
||||||
bool has_unre_flag;
|
bool has_unre_flag;
|
||||||
bool has_alt_cmd;
|
bool has_alt_cmd;
|
||||||
|
bool has_hold_field;
|
||||||
struct at_dma_slave dma_slave;
|
struct at_dma_slave dma_slave;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -187,10 +190,11 @@ static void at91_init_twi_bus(struct at91_twi_dev *dev)
|
||||||
*/
|
*/
|
||||||
static void at91_calc_twi_clock(struct at91_twi_dev *dev, int twi_clk)
|
static void at91_calc_twi_clock(struct at91_twi_dev *dev, int twi_clk)
|
||||||
{
|
{
|
||||||
int ckdiv, cdiv, div;
|
int ckdiv, cdiv, div, hold = 0;
|
||||||
struct at91_twi_pdata *pdata = dev->pdata;
|
struct at91_twi_pdata *pdata = dev->pdata;
|
||||||
int offset = pdata->clk_offset;
|
int offset = pdata->clk_offset;
|
||||||
int max_ckdiv = pdata->clk_max_div;
|
int max_ckdiv = pdata->clk_max_div;
|
||||||
|
u32 twd_hold_time_ns = 0;
|
||||||
|
|
||||||
div = max(0, (int)DIV_ROUND_UP(clk_get_rate(dev->clk),
|
div = max(0, (int)DIV_ROUND_UP(clk_get_rate(dev->clk),
|
||||||
2 * twi_clk) - offset);
|
2 * twi_clk) - offset);
|
||||||
|
@ -204,8 +208,33 @@ static void at91_calc_twi_clock(struct at91_twi_dev *dev, int twi_clk)
|
||||||
cdiv = 255;
|
cdiv = 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev->twi_cwgr_reg = (ckdiv << 16) | (cdiv << 8) | cdiv;
|
if (pdata->has_hold_field) {
|
||||||
dev_dbg(dev->dev, "cdiv %d ckdiv %d\n", cdiv, ckdiv);
|
of_property_read_u32(dev->dev->of_node, "i2c-sda-hold-time-ns",
|
||||||
|
&twd_hold_time_ns);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* hold time = HOLD + 3 x T_peripheral_clock
|
||||||
|
* Use clk rate in kHz to prevent overflows when computing
|
||||||
|
* hold.
|
||||||
|
*/
|
||||||
|
hold = DIV_ROUND_UP(twd_hold_time_ns
|
||||||
|
* (clk_get_rate(dev->clk) / 1000), 1000000);
|
||||||
|
hold -= 3;
|
||||||
|
if (hold < 0)
|
||||||
|
hold = 0;
|
||||||
|
if (hold > AT91_TWI_CWGR_HOLD_MAX) {
|
||||||
|
dev_warn(dev->dev,
|
||||||
|
"HOLD field set to its maximum value (%d instead of %d)\n",
|
||||||
|
AT91_TWI_CWGR_HOLD_MAX, hold);
|
||||||
|
hold = AT91_TWI_CWGR_HOLD_MAX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->twi_cwgr_reg = (ckdiv << 16) | (cdiv << 8) | cdiv
|
||||||
|
| AT91_TWI_CWGR_HOLD(hold);
|
||||||
|
|
||||||
|
dev_dbg(dev->dev, "cdiv %d ckdiv %d hold %d (%d ns)\n",
|
||||||
|
cdiv, ckdiv, hold, twd_hold_time_ns);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void at91_twi_dma_cleanup(struct at91_twi_dev *dev)
|
static void at91_twi_dma_cleanup(struct at91_twi_dev *dev)
|
||||||
|
@ -797,6 +826,7 @@ static struct at91_twi_pdata at91rm9200_config = {
|
||||||
.clk_offset = 3,
|
.clk_offset = 3,
|
||||||
.has_unre_flag = true,
|
.has_unre_flag = true,
|
||||||
.has_alt_cmd = false,
|
.has_alt_cmd = false,
|
||||||
|
.has_hold_field = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct at91_twi_pdata at91sam9261_config = {
|
static struct at91_twi_pdata at91sam9261_config = {
|
||||||
|
@ -804,6 +834,7 @@ static struct at91_twi_pdata at91sam9261_config = {
|
||||||
.clk_offset = 4,
|
.clk_offset = 4,
|
||||||
.has_unre_flag = false,
|
.has_unre_flag = false,
|
||||||
.has_alt_cmd = false,
|
.has_alt_cmd = false,
|
||||||
|
.has_hold_field = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct at91_twi_pdata at91sam9260_config = {
|
static struct at91_twi_pdata at91sam9260_config = {
|
||||||
|
@ -811,6 +842,7 @@ static struct at91_twi_pdata at91sam9260_config = {
|
||||||
.clk_offset = 4,
|
.clk_offset = 4,
|
||||||
.has_unre_flag = false,
|
.has_unre_flag = false,
|
||||||
.has_alt_cmd = false,
|
.has_alt_cmd = false,
|
||||||
|
.has_hold_field = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct at91_twi_pdata at91sam9g20_config = {
|
static struct at91_twi_pdata at91sam9g20_config = {
|
||||||
|
@ -818,6 +850,7 @@ static struct at91_twi_pdata at91sam9g20_config = {
|
||||||
.clk_offset = 4,
|
.clk_offset = 4,
|
||||||
.has_unre_flag = false,
|
.has_unre_flag = false,
|
||||||
.has_alt_cmd = false,
|
.has_alt_cmd = false,
|
||||||
|
.has_hold_field = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct at91_twi_pdata at91sam9g10_config = {
|
static struct at91_twi_pdata at91sam9g10_config = {
|
||||||
|
@ -825,6 +858,7 @@ static struct at91_twi_pdata at91sam9g10_config = {
|
||||||
.clk_offset = 4,
|
.clk_offset = 4,
|
||||||
.has_unre_flag = false,
|
.has_unre_flag = false,
|
||||||
.has_alt_cmd = false,
|
.has_alt_cmd = false,
|
||||||
|
.has_hold_field = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct platform_device_id at91_twi_devtypes[] = {
|
static const struct platform_device_id at91_twi_devtypes[] = {
|
||||||
|
@ -854,6 +888,15 @@ static struct at91_twi_pdata at91sam9x5_config = {
|
||||||
.clk_offset = 4,
|
.clk_offset = 4,
|
||||||
.has_unre_flag = false,
|
.has_unre_flag = false,
|
||||||
.has_alt_cmd = false,
|
.has_alt_cmd = false,
|
||||||
|
.has_hold_field = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct at91_twi_pdata sama5d4_config = {
|
||||||
|
.clk_max_div = 7,
|
||||||
|
.clk_offset = 4,
|
||||||
|
.has_unre_flag = false,
|
||||||
|
.has_alt_cmd = false,
|
||||||
|
.has_hold_field = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct at91_twi_pdata sama5d2_config = {
|
static struct at91_twi_pdata sama5d2_config = {
|
||||||
|
@ -861,6 +904,7 @@ static struct at91_twi_pdata sama5d2_config = {
|
||||||
.clk_offset = 4,
|
.clk_offset = 4,
|
||||||
.has_unre_flag = true,
|
.has_unre_flag = true,
|
||||||
.has_alt_cmd = true,
|
.has_alt_cmd = true,
|
||||||
|
.has_hold_field = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id atmel_twi_dt_ids[] = {
|
static const struct of_device_id atmel_twi_dt_ids[] = {
|
||||||
|
@ -882,6 +926,9 @@ static const struct of_device_id atmel_twi_dt_ids[] = {
|
||||||
}, {
|
}, {
|
||||||
.compatible = "atmel,at91sam9x5-i2c",
|
.compatible = "atmel,at91sam9x5-i2c",
|
||||||
.data = &at91sam9x5_config,
|
.data = &at91sam9x5_config,
|
||||||
|
}, {
|
||||||
|
.compatible = "atmel,sama5d4-i2c",
|
||||||
|
.data = &sama5d4_config,
|
||||||
}, {
|
}, {
|
||||||
.compatible = "atmel,sama5d2-i2c",
|
.compatible = "atmel,sama5d2-i2c",
|
||||||
.data = &sama5d2_config,
|
.data = &sama5d2_config,
|
||||||
|
|
|
@ -222,6 +222,15 @@ static const struct i2c_algorithm bcm2835_i2c_algo = {
|
||||||
.functionality = bcm2835_i2c_func,
|
.functionality = bcm2835_i2c_func,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This HW was reported to have problems with clock stretching:
|
||||||
|
* http://www.advamation.com/knowhow/raspberrypi/rpi-i2c-bug.html
|
||||||
|
* https://www.raspberrypi.org/forums/viewtopic.php?p=146272
|
||||||
|
*/
|
||||||
|
static const struct i2c_adapter_quirks bcm2835_i2c_quirks = {
|
||||||
|
.flags = I2C_AQ_NO_CLK_STRETCH,
|
||||||
|
};
|
||||||
|
|
||||||
static int bcm2835_i2c_probe(struct platform_device *pdev)
|
static int bcm2835_i2c_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct bcm2835_i2c_dev *i2c_dev;
|
struct bcm2835_i2c_dev *i2c_dev;
|
||||||
|
@ -293,6 +302,7 @@ static int bcm2835_i2c_probe(struct platform_device *pdev)
|
||||||
adap->algo = &bcm2835_i2c_algo;
|
adap->algo = &bcm2835_i2c_algo;
|
||||||
adap->dev.parent = &pdev->dev;
|
adap->dev.parent = &pdev->dev;
|
||||||
adap->dev.of_node = pdev->dev.of_node;
|
adap->dev.of_node = pdev->dev.of_node;
|
||||||
|
adap->quirks = &bcm2835_i2c_quirks;
|
||||||
|
|
||||||
bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, 0);
|
bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, 0);
|
||||||
|
|
||||||
|
|
|
@ -25,13 +25,16 @@
|
||||||
#include <linux/version.h>
|
#include <linux/version.h>
|
||||||
|
|
||||||
#define N_DATA_REGS 8
|
#define N_DATA_REGS 8
|
||||||
#define N_DATA_BYTES (N_DATA_REGS * 4)
|
|
||||||
|
|
||||||
/* BSC count register field definitions */
|
/*
|
||||||
#define BSC_CNT_REG1_MASK 0x0000003f
|
* PER_I2C/BSC count register mask depends on 1 byte/4 byte data register
|
||||||
#define BSC_CNT_REG1_SHIFT 0
|
* size. Cable modem and DSL SoCs with Peripheral i2c cores use 1 byte per
|
||||||
#define BSC_CNT_REG2_MASK 0x00000fc0
|
* data register whereas STB SoCs use 4 byte per data register transfer,
|
||||||
#define BSC_CNT_REG2_SHIFT 6
|
* account for this difference in total count per transaction and mask to
|
||||||
|
* use.
|
||||||
|
*/
|
||||||
|
#define BSC_CNT_REG1_MASK(nb) (nb == 1 ? GENMASK(3, 0) : GENMASK(5, 0))
|
||||||
|
#define BSC_CNT_REG1_SHIFT 0
|
||||||
|
|
||||||
/* BSC CTL register field definitions */
|
/* BSC CTL register field definitions */
|
||||||
#define BSC_CTL_REG_DTF_MASK 0x00000003
|
#define BSC_CTL_REG_DTF_MASK 0x00000003
|
||||||
|
@ -41,7 +44,7 @@
|
||||||
#define BSC_CTL_REG_INT_EN_SHIFT 6
|
#define BSC_CTL_REG_INT_EN_SHIFT 6
|
||||||
#define BSC_CTL_REG_DIV_CLK_MASK 0x00000080
|
#define BSC_CTL_REG_DIV_CLK_MASK 0x00000080
|
||||||
|
|
||||||
/* BSC_IIC_ENABLE r/w enable and interrupt field defintions */
|
/* BSC_IIC_ENABLE r/w enable and interrupt field definitions */
|
||||||
#define BSC_IIC_EN_RESTART_MASK 0x00000040
|
#define BSC_IIC_EN_RESTART_MASK 0x00000040
|
||||||
#define BSC_IIC_EN_NOSTART_MASK 0x00000020
|
#define BSC_IIC_EN_NOSTART_MASK 0x00000020
|
||||||
#define BSC_IIC_EN_NOSTOP_MASK 0x00000010
|
#define BSC_IIC_EN_NOSTOP_MASK 0x00000010
|
||||||
|
@ -169,6 +172,7 @@ struct brcmstb_i2c_dev {
|
||||||
struct completion done;
|
struct completion done;
|
||||||
bool is_suspended;
|
bool is_suspended;
|
||||||
u32 clk_freq_hz;
|
u32 clk_freq_hz;
|
||||||
|
int data_regsz;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* register accessors for both be and le cpu arch */
|
/* register accessors for both be and le cpu arch */
|
||||||
|
@ -186,6 +190,16 @@ struct brcmstb_i2c_dev {
|
||||||
#define bsc_writel(_dev, _val, _reg) \
|
#define bsc_writel(_dev, _val, _reg) \
|
||||||
__bsc_writel(_val, _dev->base + offsetof(struct bsc_regs, _reg))
|
__bsc_writel(_val, _dev->base + offsetof(struct bsc_regs, _reg))
|
||||||
|
|
||||||
|
static inline int brcmstb_i2c_get_xfersz(struct brcmstb_i2c_dev *dev)
|
||||||
|
{
|
||||||
|
return (N_DATA_REGS * dev->data_regsz);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int brcmstb_i2c_get_data_regsz(struct brcmstb_i2c_dev *dev)
|
||||||
|
{
|
||||||
|
return dev->data_regsz;
|
||||||
|
}
|
||||||
|
|
||||||
static void brcmstb_i2c_enable_disable_irq(struct brcmstb_i2c_dev *dev,
|
static void brcmstb_i2c_enable_disable_irq(struct brcmstb_i2c_dev *dev,
|
||||||
bool int_en)
|
bool int_en)
|
||||||
{
|
{
|
||||||
|
@ -323,14 +337,16 @@ static int brcmstb_i2c_xfer_bsc_data(struct brcmstb_i2c_dev *dev,
|
||||||
u8 *buf, unsigned int len,
|
u8 *buf, unsigned int len,
|
||||||
struct i2c_msg *pmsg)
|
struct i2c_msg *pmsg)
|
||||||
{
|
{
|
||||||
int cnt, byte, rc;
|
int cnt, byte, i, rc;
|
||||||
enum bsc_xfer_cmd cmd;
|
enum bsc_xfer_cmd cmd;
|
||||||
u32 ctl_reg;
|
u32 ctl_reg;
|
||||||
struct bsc_regs *pi2creg = dev->bsc_regmap;
|
struct bsc_regs *pi2creg = dev->bsc_regmap;
|
||||||
int no_ack = pmsg->flags & I2C_M_IGNORE_NAK;
|
int no_ack = pmsg->flags & I2C_M_IGNORE_NAK;
|
||||||
|
int data_regsz = brcmstb_i2c_get_data_regsz(dev);
|
||||||
|
int xfersz = brcmstb_i2c_get_xfersz(dev);
|
||||||
|
|
||||||
/* see if the transaction needs to check NACK conditions */
|
/* see if the transaction needs to check NACK conditions */
|
||||||
if (no_ack || len <= N_DATA_BYTES) {
|
if (no_ack || len <= xfersz) {
|
||||||
cmd = (pmsg->flags & I2C_M_RD) ? CMD_RD_NOACK
|
cmd = (pmsg->flags & I2C_M_RD) ? CMD_RD_NOACK
|
||||||
: CMD_WR_NOACK;
|
: CMD_WR_NOACK;
|
||||||
pi2creg->ctlhi_reg |= BSC_CTLHI_REG_IGNORE_ACK_MASK;
|
pi2creg->ctlhi_reg |= BSC_CTLHI_REG_IGNORE_ACK_MASK;
|
||||||
|
@ -348,20 +364,22 @@ static int brcmstb_i2c_xfer_bsc_data(struct brcmstb_i2c_dev *dev,
|
||||||
pi2creg->ctl_reg = ctl_reg | DTF_RD_MASK;
|
pi2creg->ctl_reg = ctl_reg | DTF_RD_MASK;
|
||||||
|
|
||||||
/* set the read/write length */
|
/* set the read/write length */
|
||||||
bsc_writel(dev, BSC_CNT_REG1_MASK & (len << BSC_CNT_REG1_SHIFT),
|
bsc_writel(dev, BSC_CNT_REG1_MASK(data_regsz) &
|
||||||
cnt_reg);
|
(len << BSC_CNT_REG1_SHIFT), cnt_reg);
|
||||||
|
|
||||||
/* Write data into data_in register */
|
/* Write data into data_in register */
|
||||||
|
|
||||||
if (cmd == CMD_WR || cmd == CMD_WR_NOACK) {
|
if (cmd == CMD_WR || cmd == CMD_WR_NOACK) {
|
||||||
for (cnt = 0; cnt < len; cnt += 4) {
|
for (cnt = 0, i = 0; cnt < len; cnt += data_regsz, i++) {
|
||||||
u32 word = 0;
|
u32 word = 0;
|
||||||
|
|
||||||
for (byte = 0; byte < 4; byte++) {
|
for (byte = 0; byte < data_regsz; byte++) {
|
||||||
word >>= 8;
|
word >>= BITS_PER_BYTE;
|
||||||
if ((cnt + byte) < len)
|
if ((cnt + byte) < len)
|
||||||
word |= buf[cnt + byte] << 24;
|
word |= buf[cnt + byte] <<
|
||||||
|
(BITS_PER_BYTE * (data_regsz - 1));
|
||||||
}
|
}
|
||||||
bsc_writel(dev, word, data_in[cnt >> 2]);
|
bsc_writel(dev, word, data_in[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -373,14 +391,15 @@ static int brcmstb_i2c_xfer_bsc_data(struct brcmstb_i2c_dev *dev,
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Read data from data_out register */
|
||||||
if (cmd == CMD_RD || cmd == CMD_RD_NOACK) {
|
if (cmd == CMD_RD || cmd == CMD_RD_NOACK) {
|
||||||
for (cnt = 0; cnt < len; cnt += 4) {
|
for (cnt = 0, i = 0; cnt < len; cnt += data_regsz, i++) {
|
||||||
u32 data = bsc_readl(dev, data_out[cnt >> 2]);
|
u32 data = bsc_readl(dev, data_out[i]);
|
||||||
|
|
||||||
for (byte = 0; byte < 4 &&
|
for (byte = 0; byte < data_regsz &&
|
||||||
(byte + cnt) < len; byte++) {
|
(byte + cnt) < len; byte++) {
|
||||||
buf[cnt + byte] = data & 0xff;
|
buf[cnt + byte] = data & 0xff;
|
||||||
data >>= 8;
|
data >>= BITS_PER_BYTE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -448,6 +467,7 @@ static int brcmstb_i2c_xfer(struct i2c_adapter *adapter,
|
||||||
int bytes_to_xfer;
|
int bytes_to_xfer;
|
||||||
u8 *tmp_buf;
|
u8 *tmp_buf;
|
||||||
int len = 0;
|
int len = 0;
|
||||||
|
int xfersz = brcmstb_i2c_get_xfersz(dev);
|
||||||
|
|
||||||
if (dev->is_suspended)
|
if (dev->is_suspended)
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
@ -482,9 +502,9 @@ static int brcmstb_i2c_xfer(struct i2c_adapter *adapter,
|
||||||
|
|
||||||
/* Perform data transfer */
|
/* Perform data transfer */
|
||||||
while (len) {
|
while (len) {
|
||||||
bytes_to_xfer = min(len, N_DATA_BYTES);
|
bytes_to_xfer = min(len, xfersz);
|
||||||
|
|
||||||
if (len <= N_DATA_BYTES && i == (num - 1))
|
if (len <= xfersz && i == (num - 1))
|
||||||
brcmstb_set_i2c_start_stop(dev,
|
brcmstb_set_i2c_start_stop(dev,
|
||||||
~(COND_START_STOP));
|
~(COND_START_STOP));
|
||||||
|
|
||||||
|
@ -542,8 +562,12 @@ static void brcmstb_i2c_set_bus_speed(struct brcmstb_i2c_dev *dev)
|
||||||
|
|
||||||
static void brcmstb_i2c_set_bsc_reg_defaults(struct brcmstb_i2c_dev *dev)
|
static void brcmstb_i2c_set_bsc_reg_defaults(struct brcmstb_i2c_dev *dev)
|
||||||
{
|
{
|
||||||
/* 4 byte data register */
|
if (brcmstb_i2c_get_data_regsz(dev) == sizeof(u32))
|
||||||
dev->bsc_regmap->ctlhi_reg = BSC_CTLHI_REG_DATAREG_SIZE_MASK;
|
/* set 4 byte data in/out xfers */
|
||||||
|
dev->bsc_regmap->ctlhi_reg = BSC_CTLHI_REG_DATAREG_SIZE_MASK;
|
||||||
|
else
|
||||||
|
dev->bsc_regmap->ctlhi_reg &= ~BSC_CTLHI_REG_DATAREG_SIZE_MASK;
|
||||||
|
|
||||||
bsc_writel(dev, dev->bsc_regmap->ctlhi_reg, ctlhi_reg);
|
bsc_writel(dev, dev->bsc_regmap->ctlhi_reg, ctlhi_reg);
|
||||||
/* set bus speed */
|
/* set bus speed */
|
||||||
brcmstb_i2c_set_bus_speed(dev);
|
brcmstb_i2c_set_bus_speed(dev);
|
||||||
|
@ -608,6 +632,13 @@ static int brcmstb_i2c_probe(struct platform_device *pdev)
|
||||||
dev->clk_freq_hz = bsc_clk[0].hz;
|
dev->clk_freq_hz = bsc_clk[0].hz;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* set the data in/out register size for compatible SoCs */
|
||||||
|
if (of_device_is_compatible(dev->device->of_node,
|
||||||
|
"brcmstb,brcmper-i2c"))
|
||||||
|
dev->data_regsz = sizeof(u8);
|
||||||
|
else
|
||||||
|
dev->data_regsz = sizeof(u32);
|
||||||
|
|
||||||
brcmstb_i2c_set_bsc_reg_defaults(dev);
|
brcmstb_i2c_set_bsc_reg_defaults(dev);
|
||||||
|
|
||||||
/* Add the i2c adapter */
|
/* Add the i2c adapter */
|
||||||
|
@ -674,6 +705,7 @@ static SIMPLE_DEV_PM_OPS(brcmstb_i2c_pm, brcmstb_i2c_suspend,
|
||||||
|
|
||||||
static const struct of_device_id brcmstb_i2c_of_match[] = {
|
static const struct of_device_id brcmstb_i2c_of_match[] = {
|
||||||
{.compatible = "brcm,brcmstb-i2c"},
|
{.compatible = "brcm,brcmstb-i2c"},
|
||||||
|
{.compatible = "brcm,brcmper-i2c"},
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, brcmstb_i2c_of_match);
|
MODULE_DEVICE_TABLE(of, brcmstb_i2c_of_match);
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
|
||||||
/* Register offsets for the I2C device. */
|
/* Register offsets for the I2C device. */
|
||||||
#define CDNS_I2C_CR_OFFSET 0x00 /* Control Register, RW */
|
#define CDNS_I2C_CR_OFFSET 0x00 /* Control Register, RW */
|
||||||
|
@ -96,6 +97,8 @@
|
||||||
CDNS_I2C_IXR_COMP)
|
CDNS_I2C_IXR_COMP)
|
||||||
|
|
||||||
#define CDNS_I2C_TIMEOUT msecs_to_jiffies(1000)
|
#define CDNS_I2C_TIMEOUT msecs_to_jiffies(1000)
|
||||||
|
/* timeout for pm runtime autosuspend */
|
||||||
|
#define CNDS_I2C_PM_TIMEOUT 1000 /* ms */
|
||||||
|
|
||||||
#define CDNS_I2C_FIFO_DEPTH 16
|
#define CDNS_I2C_FIFO_DEPTH 16
|
||||||
/* FIFO depth at which the DATA interrupt occurs */
|
/* FIFO depth at which the DATA interrupt occurs */
|
||||||
|
@ -128,7 +131,6 @@
|
||||||
* @xfer_done: Transfer complete status
|
* @xfer_done: Transfer complete status
|
||||||
* @p_send_buf: Pointer to transmit buffer
|
* @p_send_buf: Pointer to transmit buffer
|
||||||
* @p_recv_buf: Pointer to receive buffer
|
* @p_recv_buf: Pointer to receive buffer
|
||||||
* @suspended: Flag holding the device's PM status
|
|
||||||
* @send_count: Number of bytes still expected to send
|
* @send_count: Number of bytes still expected to send
|
||||||
* @recv_count: Number of bytes still expected to receive
|
* @recv_count: Number of bytes still expected to receive
|
||||||
* @curr_recv_count: Number of bytes to be received in current transfer
|
* @curr_recv_count: Number of bytes to be received in current transfer
|
||||||
|
@ -141,6 +143,7 @@
|
||||||
* @quirks: flag for broken hold bit usage in r1p10
|
* @quirks: flag for broken hold bit usage in r1p10
|
||||||
*/
|
*/
|
||||||
struct cdns_i2c {
|
struct cdns_i2c {
|
||||||
|
struct device *dev;
|
||||||
void __iomem *membase;
|
void __iomem *membase;
|
||||||
struct i2c_adapter adap;
|
struct i2c_adapter adap;
|
||||||
struct i2c_msg *p_msg;
|
struct i2c_msg *p_msg;
|
||||||
|
@ -148,7 +151,6 @@ struct cdns_i2c {
|
||||||
struct completion xfer_done;
|
struct completion xfer_done;
|
||||||
unsigned char *p_send_buf;
|
unsigned char *p_send_buf;
|
||||||
unsigned char *p_recv_buf;
|
unsigned char *p_recv_buf;
|
||||||
u8 suspended;
|
|
||||||
unsigned int send_count;
|
unsigned int send_count;
|
||||||
unsigned int recv_count;
|
unsigned int recv_count;
|
||||||
unsigned int curr_recv_count;
|
unsigned int curr_recv_count;
|
||||||
|
@ -569,9 +571,14 @@ static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||||
struct cdns_i2c *id = adap->algo_data;
|
struct cdns_i2c *id = adap->algo_data;
|
||||||
bool hold_quirk;
|
bool hold_quirk;
|
||||||
|
|
||||||
|
ret = pm_runtime_get_sync(id->dev);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
/* Check if the bus is free */
|
/* Check if the bus is free */
|
||||||
if (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) & CDNS_I2C_SR_BA)
|
if (cdns_i2c_readreg(CDNS_I2C_SR_OFFSET) & CDNS_I2C_SR_BA) {
|
||||||
return -EAGAIN;
|
ret = -EAGAIN;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
hold_quirk = !!(id->quirks & CDNS_I2C_BROKEN_HOLD_BIT);
|
hold_quirk = !!(id->quirks & CDNS_I2C_BROKEN_HOLD_BIT);
|
||||||
/*
|
/*
|
||||||
|
@ -590,7 +597,8 @@ static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||||
if (msgs[count].flags & I2C_M_RD) {
|
if (msgs[count].flags & I2C_M_RD) {
|
||||||
dev_warn(adap->dev.parent,
|
dev_warn(adap->dev.parent,
|
||||||
"Can't do repeated start after a receive message\n");
|
"Can't do repeated start after a receive message\n");
|
||||||
return -EOPNOTSUPP;
|
ret = -EOPNOTSUPP;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
id->bus_hold_flag = 1;
|
id->bus_hold_flag = 1;
|
||||||
|
@ -608,20 +616,26 @@ static int cdns_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||||
|
|
||||||
ret = cdns_i2c_process_msg(id, msgs, adap);
|
ret = cdns_i2c_process_msg(id, msgs, adap);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
goto out;
|
||||||
|
|
||||||
/* Report the other error interrupts to application */
|
/* Report the other error interrupts to application */
|
||||||
if (id->err_status) {
|
if (id->err_status) {
|
||||||
cdns_i2c_master_reset(adap);
|
cdns_i2c_master_reset(adap);
|
||||||
|
|
||||||
if (id->err_status & CDNS_I2C_IXR_NACK)
|
if (id->err_status & CDNS_I2C_IXR_NACK) {
|
||||||
return -ENXIO;
|
ret = -ENXIO;
|
||||||
|
goto out;
|
||||||
return -EIO;
|
}
|
||||||
|
ret = -EIO;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return num;
|
ret = num;
|
||||||
|
out:
|
||||||
|
pm_runtime_mark_last_busy(id->dev);
|
||||||
|
pm_runtime_put_autosuspend(id->dev);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -760,7 +774,7 @@ static int cdns_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long
|
||||||
struct clk_notifier_data *ndata = data;
|
struct clk_notifier_data *ndata = data;
|
||||||
struct cdns_i2c *id = to_cdns_i2c(nb);
|
struct cdns_i2c *id = to_cdns_i2c(nb);
|
||||||
|
|
||||||
if (id->suspended)
|
if (pm_runtime_suspended(id->dev))
|
||||||
return NOTIFY_OK;
|
return NOTIFY_OK;
|
||||||
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
|
@ -808,14 +822,12 @@ static int cdns_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long
|
||||||
*
|
*
|
||||||
* Return: 0 always
|
* Return: 0 always
|
||||||
*/
|
*/
|
||||||
static int __maybe_unused cdns_i2c_suspend(struct device *_dev)
|
static int __maybe_unused cdns_i2c_runtime_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct platform_device *pdev = container_of(_dev,
|
struct platform_device *pdev = to_platform_device(dev);
|
||||||
struct platform_device, dev);
|
|
||||||
struct cdns_i2c *xi2c = platform_get_drvdata(pdev);
|
struct cdns_i2c *xi2c = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
clk_disable(xi2c->clk);
|
clk_disable(xi2c->clk);
|
||||||
xi2c->suspended = 1;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -828,26 +840,25 @@ static int __maybe_unused cdns_i2c_suspend(struct device *_dev)
|
||||||
*
|
*
|
||||||
* Return: 0 on success and error value on error
|
* Return: 0 on success and error value on error
|
||||||
*/
|
*/
|
||||||
static int __maybe_unused cdns_i2c_resume(struct device *_dev)
|
static int __maybe_unused cdns_i2c_runtime_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct platform_device *pdev = container_of(_dev,
|
struct platform_device *pdev = to_platform_device(dev);
|
||||||
struct platform_device, dev);
|
|
||||||
struct cdns_i2c *xi2c = platform_get_drvdata(pdev);
|
struct cdns_i2c *xi2c = platform_get_drvdata(pdev);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = clk_enable(xi2c->clk);
|
ret = clk_enable(xi2c->clk);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(_dev, "Cannot enable clock.\n");
|
dev_err(dev, "Cannot enable clock.\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
xi2c->suspended = 0;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SIMPLE_DEV_PM_OPS(cdns_i2c_dev_pm_ops, cdns_i2c_suspend,
|
static const struct dev_pm_ops cdns_i2c_dev_pm_ops = {
|
||||||
cdns_i2c_resume);
|
SET_RUNTIME_PM_OPS(cdns_i2c_runtime_suspend,
|
||||||
|
cdns_i2c_runtime_resume, NULL)
|
||||||
|
};
|
||||||
|
|
||||||
static const struct cdns_platform_data r1p10_i2c_def = {
|
static const struct cdns_platform_data r1p10_i2c_def = {
|
||||||
.quirks = CDNS_I2C_BROKEN_HOLD_BIT,
|
.quirks = CDNS_I2C_BROKEN_HOLD_BIT,
|
||||||
|
@ -881,6 +892,7 @@ static int cdns_i2c_probe(struct platform_device *pdev)
|
||||||
if (!id)
|
if (!id)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
id->dev = &pdev->dev;
|
||||||
platform_set_drvdata(pdev, id);
|
platform_set_drvdata(pdev, id);
|
||||||
|
|
||||||
match = of_match_node(cdns_i2c_of_match, pdev->dev.of_node);
|
match = of_match_node(cdns_i2c_of_match, pdev->dev.of_node);
|
||||||
|
@ -913,10 +925,14 @@ static int cdns_i2c_probe(struct platform_device *pdev)
|
||||||
return PTR_ERR(id->clk);
|
return PTR_ERR(id->clk);
|
||||||
}
|
}
|
||||||
ret = clk_prepare_enable(id->clk);
|
ret = clk_prepare_enable(id->clk);
|
||||||
if (ret) {
|
if (ret)
|
||||||
dev_err(&pdev->dev, "Unable to enable clock.\n");
|
dev_err(&pdev->dev, "Unable to enable clock.\n");
|
||||||
return ret;
|
|
||||||
}
|
pm_runtime_enable(id->dev);
|
||||||
|
pm_runtime_set_autosuspend_delay(id->dev, CNDS_I2C_PM_TIMEOUT);
|
||||||
|
pm_runtime_use_autosuspend(id->dev);
|
||||||
|
pm_runtime_set_active(id->dev);
|
||||||
|
|
||||||
id->clk_rate_change_nb.notifier_call = cdns_i2c_clk_notifier_cb;
|
id->clk_rate_change_nb.notifier_call = cdns_i2c_clk_notifier_cb;
|
||||||
if (clk_notifier_register(id->clk, &id->clk_rate_change_nb))
|
if (clk_notifier_register(id->clk, &id->clk_rate_change_nb))
|
||||||
dev_warn(&pdev->dev, "Unable to register clock notifier.\n");
|
dev_warn(&pdev->dev, "Unable to register clock notifier.\n");
|
||||||
|
@ -966,6 +982,8 @@ static int cdns_i2c_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
err_clk_dis:
|
err_clk_dis:
|
||||||
clk_disable_unprepare(id->clk);
|
clk_disable_unprepare(id->clk);
|
||||||
|
pm_runtime_set_suspended(&pdev->dev);
|
||||||
|
pm_runtime_disable(&pdev->dev);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -984,6 +1002,7 @@ static int cdns_i2c_remove(struct platform_device *pdev)
|
||||||
i2c_del_adapter(&id->adap);
|
i2c_del_adapter(&id->adap);
|
||||||
clk_notifier_unregister(id->clk, &id->clk_rate_change_nb);
|
clk_notifier_unregister(id->clk, &id->clk_rate_change_nb);
|
||||||
clk_disable_unprepare(id->clk);
|
clk_disable_unprepare(id->clk);
|
||||||
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -271,6 +271,17 @@ static void __i2c_dw_enable(struct dw_i2c_dev *dev, bool enable)
|
||||||
enable ? "en" : "dis");
|
enable ? "en" : "dis");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned long i2c_dw_clk_rate(struct dw_i2c_dev *dev)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Clock is not necessary if we got LCNT/HCNT values directly from
|
||||||
|
* the platform code.
|
||||||
|
*/
|
||||||
|
if (WARN_ON_ONCE(!dev->get_clk_rate_khz))
|
||||||
|
return 0;
|
||||||
|
return dev->get_clk_rate_khz(dev);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* i2c_dw_init() - initialize the designware i2c master hardware
|
* i2c_dw_init() - initialize the designware i2c master hardware
|
||||||
* @dev: device private data
|
* @dev: device private data
|
||||||
|
@ -281,7 +292,6 @@ static void __i2c_dw_enable(struct dw_i2c_dev *dev, bool enable)
|
||||||
*/
|
*/
|
||||||
int i2c_dw_init(struct dw_i2c_dev *dev)
|
int i2c_dw_init(struct dw_i2c_dev *dev)
|
||||||
{
|
{
|
||||||
u32 input_clock_khz;
|
|
||||||
u32 hcnt, lcnt;
|
u32 hcnt, lcnt;
|
||||||
u32 reg;
|
u32 reg;
|
||||||
u32 sda_falling_time, scl_falling_time;
|
u32 sda_falling_time, scl_falling_time;
|
||||||
|
@ -295,8 +305,6 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
input_clock_khz = dev->get_clk_rate_khz(dev);
|
|
||||||
|
|
||||||
reg = dw_readl(dev, DW_IC_COMP_TYPE);
|
reg = dw_readl(dev, DW_IC_COMP_TYPE);
|
||||||
if (reg == ___constant_swab32(DW_IC_COMP_TYPE_VALUE)) {
|
if (reg == ___constant_swab32(DW_IC_COMP_TYPE_VALUE)) {
|
||||||
/* Configure register endianess access */
|
/* Configure register endianess access */
|
||||||
|
@ -325,12 +333,12 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
|
||||||
hcnt = dev->ss_hcnt;
|
hcnt = dev->ss_hcnt;
|
||||||
lcnt = dev->ss_lcnt;
|
lcnt = dev->ss_lcnt;
|
||||||
} else {
|
} else {
|
||||||
hcnt = i2c_dw_scl_hcnt(input_clock_khz,
|
hcnt = i2c_dw_scl_hcnt(i2c_dw_clk_rate(dev),
|
||||||
4000, /* tHD;STA = tHIGH = 4.0 us */
|
4000, /* tHD;STA = tHIGH = 4.0 us */
|
||||||
sda_falling_time,
|
sda_falling_time,
|
||||||
0, /* 0: DW default, 1: Ideal */
|
0, /* 0: DW default, 1: Ideal */
|
||||||
0); /* No offset */
|
0); /* No offset */
|
||||||
lcnt = i2c_dw_scl_lcnt(input_clock_khz,
|
lcnt = i2c_dw_scl_lcnt(i2c_dw_clk_rate(dev),
|
||||||
4700, /* tLOW = 4.7 us */
|
4700, /* tLOW = 4.7 us */
|
||||||
scl_falling_time,
|
scl_falling_time,
|
||||||
0); /* No offset */
|
0); /* No offset */
|
||||||
|
@ -344,12 +352,12 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
|
||||||
hcnt = dev->fs_hcnt;
|
hcnt = dev->fs_hcnt;
|
||||||
lcnt = dev->fs_lcnt;
|
lcnt = dev->fs_lcnt;
|
||||||
} else {
|
} else {
|
||||||
hcnt = i2c_dw_scl_hcnt(input_clock_khz,
|
hcnt = i2c_dw_scl_hcnt(i2c_dw_clk_rate(dev),
|
||||||
600, /* tHD;STA = tHIGH = 0.6 us */
|
600, /* tHD;STA = tHIGH = 0.6 us */
|
||||||
sda_falling_time,
|
sda_falling_time,
|
||||||
0, /* 0: DW default, 1: Ideal */
|
0, /* 0: DW default, 1: Ideal */
|
||||||
0); /* No offset */
|
0); /* No offset */
|
||||||
lcnt = i2c_dw_scl_lcnt(input_clock_khz,
|
lcnt = i2c_dw_scl_lcnt(i2c_dw_clk_rate(dev),
|
||||||
1300, /* tLOW = 1.3 us */
|
1300, /* tLOW = 1.3 us */
|
||||||
scl_falling_time,
|
scl_falling_time,
|
||||||
0); /* No offset */
|
0); /* No offset */
|
||||||
|
@ -860,6 +868,7 @@ int i2c_dw_probe(struct dw_i2c_dev *dev)
|
||||||
|
|
||||||
snprintf(adap->name, sizeof(adap->name),
|
snprintf(adap->name, sizeof(adap->name),
|
||||||
"Synopsys DesignWare I2C adapter");
|
"Synopsys DesignWare I2C adapter");
|
||||||
|
adap->retries = 3;
|
||||||
adap->algo = &i2c_dw_algo;
|
adap->algo = &i2c_dw_algo;
|
||||||
adap->dev.parent = dev->dev;
|
adap->dev.parent = dev->dev;
|
||||||
i2c_set_adapdata(adap, dev);
|
i2c_set_adapdata(adap, dev);
|
||||||
|
|
|
@ -162,7 +162,7 @@ static struct dw_pci_controller dw_pci_controllers[] = {
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
static int i2c_dw_pci_suspend(struct device *dev)
|
static int i2c_dw_pci_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
|
struct pci_dev *pdev = to_pci_dev(dev);
|
||||||
|
|
||||||
i2c_dw_disable(pci_get_drvdata(pdev));
|
i2c_dw_disable(pci_get_drvdata(pdev));
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -170,7 +170,7 @@ static int i2c_dw_pci_suspend(struct device *dev)
|
||||||
|
|
||||||
static int i2c_dw_pci_resume(struct device *dev)
|
static int i2c_dw_pci_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct pci_dev *pdev = container_of(dev, struct pci_dev, dev);
|
struct pci_dev *pdev = to_pci_dev(dev);
|
||||||
|
|
||||||
return i2c_dw_init(pci_get_drvdata(pdev));
|
return i2c_dw_init(pci_get_drvdata(pdev));
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,6 +123,7 @@ static const struct acpi_device_id dw_i2c_acpi_match[] = {
|
||||||
{ "80860F41", 0 },
|
{ "80860F41", 0 },
|
||||||
{ "808622C1", 0 },
|
{ "808622C1", 0 },
|
||||||
{ "AMD0010", ACCESS_INTR_MASK },
|
{ "AMD0010", ACCESS_INTR_MASK },
|
||||||
|
{ "AMDI0510", 0 },
|
||||||
{ "APMC0D0F", 0 },
|
{ "APMC0D0F", 0 },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
@ -134,6 +135,18 @@ static inline int dw_i2c_acpi_configure(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static int i2c_dw_plat_prepare_clk(struct dw_i2c_dev *i_dev, bool prepare)
|
||||||
|
{
|
||||||
|
if (IS_ERR(i_dev->clk))
|
||||||
|
return PTR_ERR(i_dev->clk);
|
||||||
|
|
||||||
|
if (prepare)
|
||||||
|
return clk_prepare_enable(i_dev->clk);
|
||||||
|
|
||||||
|
clk_disable_unprepare(i_dev->clk);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int dw_i2c_plat_probe(struct platform_device *pdev)
|
static int dw_i2c_plat_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct dw_i2c_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
struct dw_i2c_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||||
|
@ -206,16 +219,13 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
|
||||||
DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST;
|
DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST;
|
||||||
|
|
||||||
dev->clk = devm_clk_get(&pdev->dev, NULL);
|
dev->clk = devm_clk_get(&pdev->dev, NULL);
|
||||||
dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz;
|
if (!i2c_dw_plat_prepare_clk(dev, true)) {
|
||||||
if (IS_ERR(dev->clk))
|
dev->get_clk_rate_khz = i2c_dw_get_clk_rate_khz;
|
||||||
return PTR_ERR(dev->clk);
|
|
||||||
clk_prepare_enable(dev->clk);
|
|
||||||
|
|
||||||
if (!dev->sda_hold_time && ht) {
|
if (!dev->sda_hold_time && ht)
|
||||||
u32 ic_clk = dev->get_clk_rate_khz(dev);
|
dev->sda_hold_time = div_u64(
|
||||||
|
(u64)dev->get_clk_rate_khz(dev) * ht + 500000,
|
||||||
dev->sda_hold_time = div_u64((u64)ic_clk * ht + 500000,
|
1000000);
|
||||||
1000000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dev->tx_fifo_depth) {
|
if (!dev->tx_fifo_depth) {
|
||||||
|
@ -297,7 +307,7 @@ static int dw_i2c_plat_suspend(struct device *dev)
|
||||||
struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev);
|
struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
i2c_dw_disable(i_dev);
|
i2c_dw_disable(i_dev);
|
||||||
clk_disable_unprepare(i_dev->clk);
|
i2c_dw_plat_prepare_clk(i_dev, false);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -307,7 +317,7 @@ static int dw_i2c_plat_resume(struct device *dev)
|
||||||
struct platform_device *pdev = to_platform_device(dev);
|
struct platform_device *pdev = to_platform_device(dev);
|
||||||
struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev);
|
struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
clk_prepare_enable(i_dev->clk);
|
i2c_dw_plat_prepare_clk(i_dev, true);
|
||||||
|
|
||||||
if (!i_dev->pm_runtime_disabled)
|
if (!i_dev->pm_runtime_disabled)
|
||||||
i2c_dw_init(i_dev);
|
i2c_dw_init(i_dev);
|
||||||
|
|
|
@ -795,6 +795,7 @@ static int pch_i2c_probe(struct pci_dev *pdev,
|
||||||
/* base_addr + offset; */
|
/* base_addr + offset; */
|
||||||
adap_info->pch_data[i].pch_base_address = base_addr + 0x100 * i;
|
adap_info->pch_data[i].pch_base_address = base_addr + 0x100 * i;
|
||||||
|
|
||||||
|
pch_adap->dev.of_node = pdev->dev.of_node;
|
||||||
pch_adap->dev.parent = &pdev->dev;
|
pch_adap->dev.parent = &pdev->dev;
|
||||||
|
|
||||||
pch_i2c_init(&adap_info->pch_data[i]);
|
pch_i2c_init(&adap_info->pch_data[i]);
|
||||||
|
|
|
@ -71,6 +71,7 @@ struct em_i2c_device {
|
||||||
struct i2c_adapter adap;
|
struct i2c_adapter adap;
|
||||||
struct completion msg_done;
|
struct completion msg_done;
|
||||||
struct clk *sclk;
|
struct clk *sclk;
|
||||||
|
struct i2c_client *slave;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void em_clear_set_bit(struct em_i2c_device *priv, u8 clear, u8 set, u8 reg)
|
static inline void em_clear_set_bit(struct em_i2c_device *priv, u8 clear, u8 set, u8 reg)
|
||||||
|
@ -226,22 +227,131 @@ static int em_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||||
return num;
|
return num;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool em_i2c_slave_irq(struct em_i2c_device *priv)
|
||||||
|
{
|
||||||
|
u8 status, value;
|
||||||
|
enum i2c_slave_event event;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!priv->slave)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
status = readb(priv->base + I2C_OFS_IICSE0);
|
||||||
|
|
||||||
|
/* Extension code, do not participate */
|
||||||
|
if (status & I2C_BIT_EXC0) {
|
||||||
|
em_clear_set_bit(priv, 0, I2C_BIT_LREL0, I2C_OFS_IICC0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stop detected, we don't know if it's for slave or master */
|
||||||
|
if (status & I2C_BIT_SPD0) {
|
||||||
|
/* Notify slave device */
|
||||||
|
i2c_slave_event(priv->slave, I2C_SLAVE_STOP, &value);
|
||||||
|
/* Pretend we did not handle the interrupt */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only handle interrupts addressed to us */
|
||||||
|
if (!(status & I2C_BIT_COI0))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Enable stop interrupts */
|
||||||
|
em_clear_set_bit(priv, 0, I2C_BIT_SPIE0, I2C_OFS_IICC0);
|
||||||
|
|
||||||
|
/* Transmission or Reception */
|
||||||
|
if (status & I2C_BIT_TRC0) {
|
||||||
|
if (status & I2C_BIT_ACKD0) {
|
||||||
|
/* 9 bit interrupt mode */
|
||||||
|
em_clear_set_bit(priv, 0, I2C_BIT_WTIM0, I2C_OFS_IICC0);
|
||||||
|
|
||||||
|
/* Send data */
|
||||||
|
event = status & I2C_BIT_STD0 ?
|
||||||
|
I2C_SLAVE_READ_REQUESTED :
|
||||||
|
I2C_SLAVE_READ_PROCESSED;
|
||||||
|
i2c_slave_event(priv->slave, event, &value);
|
||||||
|
writeb(value, priv->base + I2C_OFS_IIC0);
|
||||||
|
} else {
|
||||||
|
/* NACK, stop transmitting */
|
||||||
|
em_clear_set_bit(priv, 0, I2C_BIT_LREL0, I2C_OFS_IICC0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* 8 bit interrupt mode */
|
||||||
|
em_clear_set_bit(priv, I2C_BIT_WTIM0, I2C_BIT_ACKE0,
|
||||||
|
I2C_OFS_IICC0);
|
||||||
|
em_clear_set_bit(priv, I2C_BIT_WTIM0, I2C_BIT_WREL0,
|
||||||
|
I2C_OFS_IICC0);
|
||||||
|
|
||||||
|
if (status & I2C_BIT_STD0) {
|
||||||
|
i2c_slave_event(priv->slave, I2C_SLAVE_WRITE_REQUESTED,
|
||||||
|
&value);
|
||||||
|
} else {
|
||||||
|
/* Recv data */
|
||||||
|
value = readb(priv->base + I2C_OFS_IIC0);
|
||||||
|
ret = i2c_slave_event(priv->slave,
|
||||||
|
I2C_SLAVE_WRITE_RECEIVED, &value);
|
||||||
|
if (ret < 0)
|
||||||
|
em_clear_set_bit(priv, I2C_BIT_ACKE0, 0,
|
||||||
|
I2C_OFS_IICC0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static irqreturn_t em_i2c_irq_handler(int this_irq, void *dev_id)
|
static irqreturn_t em_i2c_irq_handler(int this_irq, void *dev_id)
|
||||||
{
|
{
|
||||||
struct em_i2c_device *priv = dev_id;
|
struct em_i2c_device *priv = dev_id;
|
||||||
|
|
||||||
|
if (em_i2c_slave_irq(priv))
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
|
||||||
complete(&priv->msg_done);
|
complete(&priv->msg_done);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 em_i2c_func(struct i2c_adapter *adap)
|
static u32 em_i2c_func(struct i2c_adapter *adap)
|
||||||
{
|
{
|
||||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SLAVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int em_i2c_reg_slave(struct i2c_client *slave)
|
||||||
|
{
|
||||||
|
struct em_i2c_device *priv = i2c_get_adapdata(slave->adapter);
|
||||||
|
|
||||||
|
if (priv->slave)
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
|
if (slave->flags & I2C_CLIENT_TEN)
|
||||||
|
return -EAFNOSUPPORT;
|
||||||
|
|
||||||
|
priv->slave = slave;
|
||||||
|
|
||||||
|
/* Set slave address */
|
||||||
|
writeb(slave->addr << 1, priv->base + I2C_OFS_SVA0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int em_i2c_unreg_slave(struct i2c_client *slave)
|
||||||
|
{
|
||||||
|
struct em_i2c_device *priv = i2c_get_adapdata(slave->adapter);
|
||||||
|
|
||||||
|
WARN_ON(!priv->slave);
|
||||||
|
|
||||||
|
writeb(0, priv->base + I2C_OFS_SVA0);
|
||||||
|
|
||||||
|
priv->slave = NULL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct i2c_algorithm em_i2c_algo = {
|
static struct i2c_algorithm em_i2c_algo = {
|
||||||
.master_xfer = em_i2c_xfer,
|
.master_xfer = em_i2c_xfer,
|
||||||
.functionality = em_i2c_func,
|
.functionality = em_i2c_func,
|
||||||
|
.reg_slave = em_i2c_reg_slave,
|
||||||
|
.unreg_slave = em_i2c_unreg_slave,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int em_i2c_probe(struct platform_device *pdev)
|
static int em_i2c_probe(struct platform_device *pdev)
|
||||||
|
|
|
@ -99,7 +99,7 @@ static void dump_iic_regs(const char* header, struct ibm_iic_private* dev)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Bus timings (in ns) for bit-banging */
|
/* Bus timings (in ns) for bit-banging */
|
||||||
static struct i2c_timings {
|
static struct ibm_iic_timings {
|
||||||
unsigned int hd_sta;
|
unsigned int hd_sta;
|
||||||
unsigned int su_sto;
|
unsigned int su_sto;
|
||||||
unsigned int low;
|
unsigned int low;
|
||||||
|
@ -241,7 +241,7 @@ static int iic_dc_wait(volatile struct iic_regs __iomem *iic, u8 mask)
|
||||||
static int iic_smbus_quick(struct ibm_iic_private* dev, const struct i2c_msg* p)
|
static int iic_smbus_quick(struct ibm_iic_private* dev, const struct i2c_msg* p)
|
||||||
{
|
{
|
||||||
volatile struct iic_regs __iomem *iic = dev->vaddr;
|
volatile struct iic_regs __iomem *iic = dev->vaddr;
|
||||||
const struct i2c_timings* t = &timings[dev->fast_mode ? 1 : 0];
|
const struct ibm_iic_timings *t = &timings[dev->fast_mode ? 1 : 0];
|
||||||
u8 mask, v, sda;
|
u8 mask, v, sda;
|
||||||
int i, res;
|
int i, res;
|
||||||
|
|
||||||
|
|
|
@ -151,10 +151,11 @@
|
||||||
#define INT_FIFO_EMPTYING BIT(12)
|
#define INT_FIFO_EMPTYING BIT(12)
|
||||||
#define INT_TRANSACTION_DONE BIT(15)
|
#define INT_TRANSACTION_DONE BIT(15)
|
||||||
#define INT_SLAVE_EVENT BIT(16)
|
#define INT_SLAVE_EVENT BIT(16)
|
||||||
|
#define INT_MASTER_HALTED BIT(17)
|
||||||
#define INT_TIMING BIT(18)
|
#define INT_TIMING BIT(18)
|
||||||
|
#define INT_STOP_DETECTED BIT(19)
|
||||||
|
|
||||||
#define INT_FIFO_FULL_FILLING (INT_FIFO_FULL | INT_FIFO_FILLING)
|
#define INT_FIFO_FULL_FILLING (INT_FIFO_FULL | INT_FIFO_FILLING)
|
||||||
#define INT_FIFO_EMPTY_EMPTYING (INT_FIFO_EMPTY | INT_FIFO_EMPTYING)
|
|
||||||
|
|
||||||
/* Level interrupts need clearing after handling instead of before */
|
/* Level interrupts need clearing after handling instead of before */
|
||||||
#define INT_LEVEL 0x01e00
|
#define INT_LEVEL 0x01e00
|
||||||
|
@ -177,7 +178,8 @@
|
||||||
INT_FIFO_FULL | \
|
INT_FIFO_FULL | \
|
||||||
INT_FIFO_FILLING | \
|
INT_FIFO_FILLING | \
|
||||||
INT_FIFO_EMPTY | \
|
INT_FIFO_EMPTY | \
|
||||||
INT_FIFO_EMPTYING)
|
INT_MASTER_HALTED | \
|
||||||
|
INT_STOP_DETECTED)
|
||||||
|
|
||||||
#define INT_ENABLE_MASK_WAITSTOP (INT_SLAVE_EVENT | \
|
#define INT_ENABLE_MASK_WAITSTOP (INT_SLAVE_EVENT | \
|
||||||
INT_ADDR_ACK_ERR | \
|
INT_ADDR_ACK_ERR | \
|
||||||
|
@ -511,7 +513,17 @@ static void img_i2c_soft_reset(struct img_i2c *i2c)
|
||||||
SCB_CONTROL_CLK_ENABLE | SCB_CONTROL_SOFT_RESET);
|
SCB_CONTROL_CLK_ENABLE | SCB_CONTROL_SOFT_RESET);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* enable or release transaction halt for control of repeated starts */
|
/*
|
||||||
|
* Enable or release transaction halt for control of repeated starts.
|
||||||
|
* In version 3.3 of the IP when transaction halt is set, an interrupt
|
||||||
|
* will be generated after each byte of a transfer instead of after
|
||||||
|
* every transfer but before the stop bit.
|
||||||
|
* Due to this behaviour we have to be careful that every time we
|
||||||
|
* release the transaction halt we have to re-enable it straight away
|
||||||
|
* so that we only process a single byte, not doing so will result in
|
||||||
|
* all remaining bytes been processed and a stop bit being issued,
|
||||||
|
* which will prevent us having a repeated start.
|
||||||
|
*/
|
||||||
static void img_i2c_transaction_halt(struct img_i2c *i2c, bool t_halt)
|
static void img_i2c_transaction_halt(struct img_i2c *i2c, bool t_halt)
|
||||||
{
|
{
|
||||||
u32 val;
|
u32 val;
|
||||||
|
@ -580,7 +592,6 @@ static void img_i2c_read(struct img_i2c *i2c)
|
||||||
img_i2c_writel(i2c, SCB_READ_ADDR_REG, i2c->msg.addr);
|
img_i2c_writel(i2c, SCB_READ_ADDR_REG, i2c->msg.addr);
|
||||||
img_i2c_writel(i2c, SCB_READ_COUNT_REG, i2c->msg.len);
|
img_i2c_writel(i2c, SCB_READ_COUNT_REG, i2c->msg.len);
|
||||||
|
|
||||||
img_i2c_transaction_halt(i2c, false);
|
|
||||||
mod_timer(&i2c->check_timer, jiffies + msecs_to_jiffies(1));
|
mod_timer(&i2c->check_timer, jiffies + msecs_to_jiffies(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -594,7 +605,6 @@ static void img_i2c_write(struct img_i2c *i2c)
|
||||||
img_i2c_writel(i2c, SCB_WRITE_ADDR_REG, i2c->msg.addr);
|
img_i2c_writel(i2c, SCB_WRITE_ADDR_REG, i2c->msg.addr);
|
||||||
img_i2c_writel(i2c, SCB_WRITE_COUNT_REG, i2c->msg.len);
|
img_i2c_writel(i2c, SCB_WRITE_COUNT_REG, i2c->msg.len);
|
||||||
|
|
||||||
img_i2c_transaction_halt(i2c, false);
|
|
||||||
mod_timer(&i2c->check_timer, jiffies + msecs_to_jiffies(1));
|
mod_timer(&i2c->check_timer, jiffies + msecs_to_jiffies(1));
|
||||||
img_i2c_write_fifo(i2c);
|
img_i2c_write_fifo(i2c);
|
||||||
|
|
||||||
|
@ -750,7 +760,9 @@ static unsigned int img_i2c_atomic(struct img_i2c *i2c,
|
||||||
next_cmd = CMD_RET_ACK;
|
next_cmd = CMD_RET_ACK;
|
||||||
break;
|
break;
|
||||||
case CMD_RET_ACK:
|
case CMD_RET_ACK:
|
||||||
if (i2c->line_status & LINESTAT_ACK_DET) {
|
if (i2c->line_status & LINESTAT_ACK_DET ||
|
||||||
|
(i2c->line_status & LINESTAT_NACK_DET &&
|
||||||
|
i2c->msg.flags & I2C_M_IGNORE_NAK)) {
|
||||||
if (i2c->msg.len == 0) {
|
if (i2c->msg.len == 0) {
|
||||||
next_cmd = CMD_GEN_STOP;
|
next_cmd = CMD_GEN_STOP;
|
||||||
} else if (i2c->msg.flags & I2C_M_RD) {
|
} else if (i2c->msg.flags & I2C_M_RD) {
|
||||||
|
@ -858,34 +870,42 @@ static unsigned int img_i2c_auto(struct img_i2c *i2c,
|
||||||
|
|
||||||
/* Enable transaction halt on start bit */
|
/* Enable transaction halt on start bit */
|
||||||
if (!i2c->last_msg && line_status & LINESTAT_START_BIT_DET) {
|
if (!i2c->last_msg && line_status & LINESTAT_START_BIT_DET) {
|
||||||
img_i2c_transaction_halt(i2c, true);
|
img_i2c_transaction_halt(i2c, !i2c->last_msg);
|
||||||
/* we're no longer interested in the slave event */
|
/* we're no longer interested in the slave event */
|
||||||
i2c->int_enable &= ~INT_SLAVE_EVENT;
|
i2c->int_enable &= ~INT_SLAVE_EVENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
mod_timer(&i2c->check_timer, jiffies + msecs_to_jiffies(1));
|
mod_timer(&i2c->check_timer, jiffies + msecs_to_jiffies(1));
|
||||||
|
|
||||||
|
if (int_status & INT_STOP_DETECTED) {
|
||||||
|
/* Drain remaining data in FIFO and complete transaction */
|
||||||
|
if (i2c->msg.flags & I2C_M_RD)
|
||||||
|
img_i2c_read_fifo(i2c);
|
||||||
|
return ISR_COMPLETE(0);
|
||||||
|
}
|
||||||
|
|
||||||
if (i2c->msg.flags & I2C_M_RD) {
|
if (i2c->msg.flags & I2C_M_RD) {
|
||||||
if (int_status & INT_FIFO_FULL_FILLING) {
|
if (int_status & (INT_FIFO_FULL_FILLING | INT_MASTER_HALTED)) {
|
||||||
img_i2c_read_fifo(i2c);
|
img_i2c_read_fifo(i2c);
|
||||||
if (i2c->msg.len == 0)
|
if (i2c->msg.len == 0)
|
||||||
return ISR_WAITSTOP;
|
return ISR_WAITSTOP;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (int_status & INT_FIFO_EMPTY_EMPTYING) {
|
if (int_status & (INT_FIFO_EMPTY | INT_MASTER_HALTED)) {
|
||||||
/*
|
|
||||||
* The write fifo empty indicates that we're in the
|
|
||||||
* last byte so it's safe to start a new write
|
|
||||||
* transaction without losing any bytes from the
|
|
||||||
* previous one.
|
|
||||||
* see 2.3.7 Repeated Start Transactions.
|
|
||||||
*/
|
|
||||||
if ((int_status & INT_FIFO_EMPTY) &&
|
if ((int_status & INT_FIFO_EMPTY) &&
|
||||||
i2c->msg.len == 0)
|
i2c->msg.len == 0)
|
||||||
return ISR_WAITSTOP;
|
return ISR_WAITSTOP;
|
||||||
img_i2c_write_fifo(i2c);
|
img_i2c_write_fifo(i2c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (int_status & INT_MASTER_HALTED) {
|
||||||
|
/*
|
||||||
|
* Release and then enable transaction halt, to
|
||||||
|
* allow only a single byte to proceed.
|
||||||
|
*/
|
||||||
|
img_i2c_transaction_halt(i2c, false);
|
||||||
|
img_i2c_transaction_halt(i2c, !i2c->last_msg);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1017,20 +1037,23 @@ static int img_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
for (i = 0; i < num; i++) {
|
for (i = 0; i < num; i++) {
|
||||||
if (likely(msgs[i].len))
|
|
||||||
continue;
|
|
||||||
/*
|
/*
|
||||||
* 0 byte reads are not possible because the slave could try
|
* 0 byte reads are not possible because the slave could try
|
||||||
* and pull the data line low, preventing a stop bit.
|
* and pull the data line low, preventing a stop bit.
|
||||||
*/
|
*/
|
||||||
if (unlikely(msgs[i].flags & I2C_M_RD))
|
if (!msgs[i].len && msgs[i].flags & I2C_M_RD)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
/*
|
/*
|
||||||
* 0 byte writes are possible and used for probing, but we
|
* 0 byte writes are possible and used for probing, but we
|
||||||
* cannot do them in automatic mode, so use atomic mode
|
* cannot do them in automatic mode, so use atomic mode
|
||||||
* instead.
|
* instead.
|
||||||
|
*
|
||||||
|
* Also, the I2C_M_IGNORE_NAK mode can only be implemented
|
||||||
|
* in atomic mode.
|
||||||
*/
|
*/
|
||||||
atomic = true;
|
if (!msgs[i].len ||
|
||||||
|
(msgs[i].flags & I2C_M_IGNORE_NAK))
|
||||||
|
atomic = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = clk_prepare_enable(i2c->scb_clk);
|
ret = clk_prepare_enable(i2c->scb_clk);
|
||||||
|
@ -1069,12 +1092,31 @@ static int img_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||||
img_i2c_writel(i2c, SCB_INT_CLEAR_REG, ~0);
|
img_i2c_writel(i2c, SCB_INT_CLEAR_REG, ~0);
|
||||||
img_i2c_writel(i2c, SCB_CLEAR_REG, ~0);
|
img_i2c_writel(i2c, SCB_CLEAR_REG, ~0);
|
||||||
|
|
||||||
if (atomic)
|
if (atomic) {
|
||||||
img_i2c_atomic_start(i2c);
|
img_i2c_atomic_start(i2c);
|
||||||
else if (msg->flags & I2C_M_RD)
|
} else {
|
||||||
img_i2c_read(i2c);
|
/*
|
||||||
else
|
* Enable transaction halt if not the last message in
|
||||||
img_i2c_write(i2c);
|
* the queue so that we can control repeated starts.
|
||||||
|
*/
|
||||||
|
img_i2c_transaction_halt(i2c, !i2c->last_msg);
|
||||||
|
|
||||||
|
if (msg->flags & I2C_M_RD)
|
||||||
|
img_i2c_read(i2c);
|
||||||
|
else
|
||||||
|
img_i2c_write(i2c);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Release and then enable transaction halt, to
|
||||||
|
* allow only a single byte to proceed.
|
||||||
|
* This doesn't have an effect on the initial transfer
|
||||||
|
* but will allow the following transfers to start
|
||||||
|
* processing if the previous transfer was marked as
|
||||||
|
* complete while the i2c block was halted.
|
||||||
|
*/
|
||||||
|
img_i2c_transaction_halt(i2c, false);
|
||||||
|
img_i2c_transaction_halt(i2c, !i2c->last_msg);
|
||||||
|
}
|
||||||
spin_unlock_irqrestore(&i2c->lock, flags);
|
spin_unlock_irqrestore(&i2c->lock, flags);
|
||||||
|
|
||||||
time_left = wait_for_completion_timeout(&i2c->msg_complete,
|
time_left = wait_for_completion_timeout(&i2c->msg_complete,
|
||||||
|
|
|
@ -29,9 +29,6 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Includes *******************************************************************
|
|
||||||
*******************************************************************************/
|
|
||||||
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/completion.h>
|
#include <linux/completion.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
@ -53,12 +50,10 @@
|
||||||
#include <linux/pinctrl/consumer.h>
|
#include <linux/pinctrl/consumer.h>
|
||||||
#include <linux/platform_data/i2c-imx.h>
|
#include <linux/platform_data/i2c-imx.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
|
||||||
/** Defines ********************************************************************
|
|
||||||
*******************************************************************************/
|
|
||||||
|
|
||||||
/* This will be the driver name the kernel reports */
|
/* This will be the driver name the kernel reports */
|
||||||
#define DRIVER_NAME "imx-i2c"
|
#define DRIVER_NAME "imx-i2c"
|
||||||
|
|
||||||
|
@ -120,8 +115,7 @@
|
||||||
#define I2CR_IEN_OPCODE_0 0x0
|
#define I2CR_IEN_OPCODE_0 0x0
|
||||||
#define I2CR_IEN_OPCODE_1 I2CR_IEN
|
#define I2CR_IEN_OPCODE_1 I2CR_IEN
|
||||||
|
|
||||||
/** Variables ******************************************************************
|
#define I2C_PM_TIMEOUT 10 /* ms */
|
||||||
*******************************************************************************/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* sorted list of clock divider, register value pairs
|
* sorted list of clock divider, register value pairs
|
||||||
|
@ -346,7 +340,7 @@ fail_tx:
|
||||||
dma_release_channel(dma->chan_tx);
|
dma_release_channel(dma->chan_tx);
|
||||||
fail_al:
|
fail_al:
|
||||||
devm_kfree(dev, dma);
|
devm_kfree(dev, dma);
|
||||||
dev_info(dev, "can't use DMA\n");
|
dev_info(dev, "can't use DMA, using PIO instead.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void i2c_imx_dma_callback(void *arg)
|
static void i2c_imx_dma_callback(void *arg)
|
||||||
|
@ -393,6 +387,7 @@ static int i2c_imx_dma_xfer(struct imx_i2c_struct *i2c_imx,
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_submit:
|
err_submit:
|
||||||
|
dmaengine_terminate_all(dma->chan_using);
|
||||||
err_desc:
|
err_desc:
|
||||||
dma_unmap_single(chan_dev, dma->dma_buf,
|
dma_unmap_single(chan_dev, dma->dma_buf,
|
||||||
dma->dma_len, dma->dma_data_dir);
|
dma->dma_len, dma->dma_data_dir);
|
||||||
|
@ -416,9 +411,6 @@ static void i2c_imx_dma_free(struct imx_i2c_struct *i2c_imx)
|
||||||
dma->chan_using = NULL;
|
dma->chan_using = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Functions for IMX I2C adapter driver ***************************************
|
|
||||||
*******************************************************************************/
|
|
||||||
|
|
||||||
static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy)
|
static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy)
|
||||||
{
|
{
|
||||||
unsigned long orig_jiffies = jiffies;
|
unsigned long orig_jiffies = jiffies;
|
||||||
|
@ -527,9 +519,6 @@ static int i2c_imx_start(struct imx_i2c_struct *i2c_imx)
|
||||||
|
|
||||||
i2c_imx_set_clk(i2c_imx);
|
i2c_imx_set_clk(i2c_imx);
|
||||||
|
|
||||||
result = clk_prepare_enable(i2c_imx->clk);
|
|
||||||
if (result)
|
|
||||||
return result;
|
|
||||||
imx_i2c_write_reg(i2c_imx->ifdr, i2c_imx, IMX_I2C_IFDR);
|
imx_i2c_write_reg(i2c_imx->ifdr, i2c_imx, IMX_I2C_IFDR);
|
||||||
/* Enable I2C controller */
|
/* Enable I2C controller */
|
||||||
imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);
|
imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);
|
||||||
|
@ -582,7 +571,6 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx)
|
||||||
/* Disable I2C controller */
|
/* Disable I2C controller */
|
||||||
temp = i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
|
temp = i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
|
||||||
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
||||||
clk_disable_unprepare(i2c_imx->clk);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
|
static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
|
||||||
|
@ -901,6 +889,10 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter,
|
||||||
|
|
||||||
dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
|
dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
|
||||||
|
|
||||||
|
result = pm_runtime_get_sync(i2c_imx->adapter.dev.parent);
|
||||||
|
if (result < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
/* Start I2C transfer */
|
/* Start I2C transfer */
|
||||||
result = i2c_imx_start(i2c_imx);
|
result = i2c_imx_start(i2c_imx);
|
||||||
if (result) {
|
if (result) {
|
||||||
|
@ -964,6 +956,10 @@ fail0:
|
||||||
/* Stop I2C transfer */
|
/* Stop I2C transfer */
|
||||||
i2c_imx_stop(i2c_imx);
|
i2c_imx_stop(i2c_imx);
|
||||||
|
|
||||||
|
pm_runtime_mark_last_busy(i2c_imx->adapter.dev.parent);
|
||||||
|
pm_runtime_put_autosuspend(i2c_imx->adapter.dev.parent);
|
||||||
|
|
||||||
|
out:
|
||||||
dev_dbg(&i2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__,
|
dev_dbg(&i2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__,
|
||||||
(result < 0) ? "error" : "success msg",
|
(result < 0) ? "error" : "success msg",
|
||||||
(result < 0) ? result : num);
|
(result < 0) ? result : num);
|
||||||
|
@ -997,10 +993,8 @@ static void i2c_imx_init_recovery_info(struct imx_i2c_struct *i2c_imx,
|
||||||
PINCTRL_STATE_DEFAULT);
|
PINCTRL_STATE_DEFAULT);
|
||||||
i2c_imx->pinctrl_pins_gpio = pinctrl_lookup_state(i2c_imx->pinctrl,
|
i2c_imx->pinctrl_pins_gpio = pinctrl_lookup_state(i2c_imx->pinctrl,
|
||||||
"gpio");
|
"gpio");
|
||||||
rinfo->sda_gpio = of_get_named_gpio_flags(pdev->dev.of_node,
|
rinfo->sda_gpio = of_get_named_gpio(pdev->dev.of_node, "sda-gpios", 0);
|
||||||
"sda-gpios", 0, NULL);
|
rinfo->scl_gpio = of_get_named_gpio(pdev->dev.of_node, "scl-gpios", 0);
|
||||||
rinfo->scl_gpio = of_get_named_gpio_flags(pdev->dev.of_node,
|
|
||||||
"scl-gpios", 0, NULL);
|
|
||||||
|
|
||||||
if (!gpio_is_valid(rinfo->sda_gpio) ||
|
if (!gpio_is_valid(rinfo->sda_gpio) ||
|
||||||
!gpio_is_valid(rinfo->scl_gpio) ||
|
!gpio_is_valid(rinfo->scl_gpio) ||
|
||||||
|
@ -1083,7 +1077,7 @@ static int i2c_imx_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
ret = clk_prepare_enable(i2c_imx->clk);
|
ret = clk_prepare_enable(i2c_imx->clk);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "can't enable I2C clock\n");
|
dev_err(&pdev->dev, "can't enable I2C clock, ret=%d\n", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1107,6 +1101,18 @@ static int i2c_imx_probe(struct platform_device *pdev)
|
||||||
/* Set up adapter data */
|
/* Set up adapter data */
|
||||||
i2c_set_adapdata(&i2c_imx->adapter, i2c_imx);
|
i2c_set_adapdata(&i2c_imx->adapter, i2c_imx);
|
||||||
|
|
||||||
|
/* Set up platform driver data */
|
||||||
|
platform_set_drvdata(pdev, i2c_imx);
|
||||||
|
|
||||||
|
pm_runtime_set_autosuspend_delay(&pdev->dev, I2C_PM_TIMEOUT);
|
||||||
|
pm_runtime_use_autosuspend(&pdev->dev);
|
||||||
|
pm_runtime_set_active(&pdev->dev);
|
||||||
|
pm_runtime_enable(&pdev->dev);
|
||||||
|
|
||||||
|
ret = pm_runtime_get_sync(&pdev->dev);
|
||||||
|
if (ret < 0)
|
||||||
|
goto rpm_disable;
|
||||||
|
|
||||||
/* Set up clock divider */
|
/* Set up clock divider */
|
||||||
i2c_imx->bitrate = IMX_I2C_BIT_RATE;
|
i2c_imx->bitrate = IMX_I2C_BIT_RATE;
|
||||||
ret = of_property_read_u32(pdev->dev.of_node,
|
ret = of_property_read_u32(pdev->dev.of_node,
|
||||||
|
@ -1125,12 +1131,11 @@ static int i2c_imx_probe(struct platform_device *pdev)
|
||||||
ret = i2c_add_numbered_adapter(&i2c_imx->adapter);
|
ret = i2c_add_numbered_adapter(&i2c_imx->adapter);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "registration failed\n");
|
dev_err(&pdev->dev, "registration failed\n");
|
||||||
goto clk_disable;
|
goto rpm_disable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set up platform driver data */
|
pm_runtime_mark_last_busy(&pdev->dev);
|
||||||
platform_set_drvdata(pdev, i2c_imx);
|
pm_runtime_put_autosuspend(&pdev->dev);
|
||||||
clk_disable_unprepare(i2c_imx->clk);
|
|
||||||
|
|
||||||
dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", irq);
|
dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", irq);
|
||||||
dev_dbg(&i2c_imx->adapter.dev, "device resources: %pR\n", res);
|
dev_dbg(&i2c_imx->adapter.dev, "device resources: %pR\n", res);
|
||||||
|
@ -1143,6 +1148,12 @@ static int i2c_imx_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
return 0; /* Return OK */
|
return 0; /* Return OK */
|
||||||
|
|
||||||
|
rpm_disable:
|
||||||
|
pm_runtime_put_noidle(&pdev->dev);
|
||||||
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
pm_runtime_set_suspended(&pdev->dev);
|
||||||
|
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||||
|
|
||||||
clk_disable:
|
clk_disable:
|
||||||
clk_disable_unprepare(i2c_imx->clk);
|
clk_disable_unprepare(i2c_imx->clk);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1151,6 +1162,11 @@ clk_disable:
|
||||||
static int i2c_imx_remove(struct platform_device *pdev)
|
static int i2c_imx_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct imx_i2c_struct *i2c_imx = platform_get_drvdata(pdev);
|
struct imx_i2c_struct *i2c_imx = platform_get_drvdata(pdev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = pm_runtime_get_sync(&pdev->dev);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
/* remove adapter */
|
/* remove adapter */
|
||||||
dev_dbg(&i2c_imx->adapter.dev, "adapter removed\n");
|
dev_dbg(&i2c_imx->adapter.dev, "adapter removed\n");
|
||||||
|
@ -1165,17 +1181,54 @@ static int i2c_imx_remove(struct platform_device *pdev)
|
||||||
imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2CR);
|
imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2CR);
|
||||||
imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2SR);
|
imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2SR);
|
||||||
|
|
||||||
|
clk_disable_unprepare(i2c_imx->clk);
|
||||||
|
|
||||||
|
pm_runtime_put_noidle(&pdev->dev);
|
||||||
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
static int i2c_imx_runtime_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
clk_disable_unprepare(i2c_imx->clk);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int i2c_imx_runtime_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(i2c_imx->clk);
|
||||||
|
if (ret)
|
||||||
|
dev_err(dev, "can't enable I2C clock, ret=%d\n", ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct dev_pm_ops i2c_imx_pm_ops = {
|
||||||
|
SET_RUNTIME_PM_OPS(i2c_imx_runtime_suspend,
|
||||||
|
i2c_imx_runtime_resume, NULL)
|
||||||
|
};
|
||||||
|
#define I2C_IMX_PM_OPS (&i2c_imx_pm_ops)
|
||||||
|
#else
|
||||||
|
#define I2C_IMX_PM_OPS NULL
|
||||||
|
#endif /* CONFIG_PM */
|
||||||
|
|
||||||
static struct platform_driver i2c_imx_driver = {
|
static struct platform_driver i2c_imx_driver = {
|
||||||
.probe = i2c_imx_probe,
|
.probe = i2c_imx_probe,
|
||||||
.remove = i2c_imx_remove,
|
.remove = i2c_imx_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = DRIVER_NAME,
|
.name = DRIVER_NAME,
|
||||||
|
.pm = I2C_IMX_PM_OPS,
|
||||||
.of_match_table = i2c_imx_dt_ids,
|
.of_match_table = i2c_imx_dt_ids,
|
||||||
},
|
},
|
||||||
.id_table = imx_i2c_devtype,
|
.id_table = imx_i2c_devtype,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init i2c_adap_imx_init(void)
|
static int __init i2c_adap_imx_init(void)
|
||||||
|
|
|
@ -132,6 +132,7 @@ struct mtk_i2c_compatible {
|
||||||
unsigned char pmic_i2c: 1;
|
unsigned char pmic_i2c: 1;
|
||||||
unsigned char dcm: 1;
|
unsigned char dcm: 1;
|
||||||
unsigned char auto_restart: 1;
|
unsigned char auto_restart: 1;
|
||||||
|
unsigned char aux_len_reg: 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mtk_i2c {
|
struct mtk_i2c {
|
||||||
|
@ -153,6 +154,8 @@ struct mtk_i2c {
|
||||||
enum mtk_trans_op op;
|
enum mtk_trans_op op;
|
||||||
u16 timing_reg;
|
u16 timing_reg;
|
||||||
u16 high_speed_reg;
|
u16 high_speed_reg;
|
||||||
|
unsigned char auto_restart;
|
||||||
|
bool ignore_restart_irq;
|
||||||
const struct mtk_i2c_compatible *dev_comp;
|
const struct mtk_i2c_compatible *dev_comp;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -178,6 +181,7 @@ static const struct mtk_i2c_compatible mt6577_compat = {
|
||||||
.pmic_i2c = 0,
|
.pmic_i2c = 0,
|
||||||
.dcm = 1,
|
.dcm = 1,
|
||||||
.auto_restart = 0,
|
.auto_restart = 0,
|
||||||
|
.aux_len_reg = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct mtk_i2c_compatible mt6589_compat = {
|
static const struct mtk_i2c_compatible mt6589_compat = {
|
||||||
|
@ -185,6 +189,7 @@ static const struct mtk_i2c_compatible mt6589_compat = {
|
||||||
.pmic_i2c = 1,
|
.pmic_i2c = 1,
|
||||||
.dcm = 0,
|
.dcm = 0,
|
||||||
.auto_restart = 0,
|
.auto_restart = 0,
|
||||||
|
.aux_len_reg = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct mtk_i2c_compatible mt8173_compat = {
|
static const struct mtk_i2c_compatible mt8173_compat = {
|
||||||
|
@ -192,6 +197,7 @@ static const struct mtk_i2c_compatible mt8173_compat = {
|
||||||
.pmic_i2c = 0,
|
.pmic_i2c = 0,
|
||||||
.dcm = 1,
|
.dcm = 1,
|
||||||
.auto_restart = 1,
|
.auto_restart = 1,
|
||||||
|
.aux_len_reg = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id mtk_i2c_of_match[] = {
|
static const struct of_device_id mtk_i2c_of_match[] = {
|
||||||
|
@ -373,7 +379,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
|
||||||
|
|
||||||
i2c->irq_stat = 0;
|
i2c->irq_stat = 0;
|
||||||
|
|
||||||
if (i2c->dev_comp->auto_restart)
|
if (i2c->auto_restart)
|
||||||
restart_flag = I2C_RS_TRANSFER;
|
restart_flag = I2C_RS_TRANSFER;
|
||||||
|
|
||||||
reinit_completion(&i2c->msg_complete);
|
reinit_completion(&i2c->msg_complete);
|
||||||
|
@ -411,8 +417,14 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
|
||||||
|
|
||||||
/* Set transfer and transaction len */
|
/* Set transfer and transaction len */
|
||||||
if (i2c->op == I2C_MASTER_WRRD) {
|
if (i2c->op == I2C_MASTER_WRRD) {
|
||||||
writew(msgs->len | ((msgs + 1)->len) << 8,
|
if (i2c->dev_comp->aux_len_reg) {
|
||||||
i2c->base + OFFSET_TRANSFER_LEN);
|
writew(msgs->len, i2c->base + OFFSET_TRANSFER_LEN);
|
||||||
|
writew((msgs + 1)->len, i2c->base +
|
||||||
|
OFFSET_TRANSFER_LEN_AUX);
|
||||||
|
} else {
|
||||||
|
writew(msgs->len | ((msgs + 1)->len) << 8,
|
||||||
|
i2c->base + OFFSET_TRANSFER_LEN);
|
||||||
|
}
|
||||||
writew(I2C_WRRD_TRANAC_VALUE, i2c->base + OFFSET_TRANSAC_LEN);
|
writew(I2C_WRRD_TRANAC_VALUE, i2c->base + OFFSET_TRANSAC_LEN);
|
||||||
} else {
|
} else {
|
||||||
writew(msgs->len, i2c->base + OFFSET_TRANSFER_LEN);
|
writew(msgs->len, i2c->base + OFFSET_TRANSFER_LEN);
|
||||||
|
@ -461,7 +473,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
|
||||||
|
|
||||||
writel(I2C_DMA_START_EN, i2c->pdmabase + OFFSET_EN);
|
writel(I2C_DMA_START_EN, i2c->pdmabase + OFFSET_EN);
|
||||||
|
|
||||||
if (!i2c->dev_comp->auto_restart) {
|
if (!i2c->auto_restart) {
|
||||||
start_reg = I2C_TRANSAC_START;
|
start_reg = I2C_TRANSAC_START;
|
||||||
} else {
|
} else {
|
||||||
start_reg = I2C_TRANSAC_START | I2C_RS_MUL_TRIG;
|
start_reg = I2C_TRANSAC_START | I2C_RS_MUL_TRIG;
|
||||||
|
@ -518,6 +530,24 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap,
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
i2c->auto_restart = i2c->dev_comp->auto_restart;
|
||||||
|
|
||||||
|
/* checking if we can skip restart and optimize using WRRD mode */
|
||||||
|
if (i2c->auto_restart && num == 2) {
|
||||||
|
if (!(msgs[0].flags & I2C_M_RD) && (msgs[1].flags & I2C_M_RD) &&
|
||||||
|
msgs[0].addr == msgs[1].addr) {
|
||||||
|
i2c->auto_restart = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i2c->auto_restart && num >= 2 && i2c->speed_hz > MAX_FS_MODE_SPEED)
|
||||||
|
/* ignore the first restart irq after the master code,
|
||||||
|
* otherwise the first transfer will be discarded.
|
||||||
|
*/
|
||||||
|
i2c->ignore_restart_irq = true;
|
||||||
|
else
|
||||||
|
i2c->ignore_restart_irq = false;
|
||||||
|
|
||||||
while (left_num--) {
|
while (left_num--) {
|
||||||
if (!msgs->buf) {
|
if (!msgs->buf) {
|
||||||
dev_dbg(i2c->dev, "data buffer is NULL.\n");
|
dev_dbg(i2c->dev, "data buffer is NULL.\n");
|
||||||
|
@ -530,7 +560,7 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap,
|
||||||
else
|
else
|
||||||
i2c->op = I2C_MASTER_WR;
|
i2c->op = I2C_MASTER_WR;
|
||||||
|
|
||||||
if (!i2c->dev_comp->auto_restart) {
|
if (!i2c->auto_restart) {
|
||||||
if (num > 1) {
|
if (num > 1) {
|
||||||
/* combined two messages into one transaction */
|
/* combined two messages into one transaction */
|
||||||
i2c->op = I2C_MASTER_WRRD;
|
i2c->op = I2C_MASTER_WRRD;
|
||||||
|
@ -559,7 +589,7 @@ static irqreturn_t mtk_i2c_irq(int irqno, void *dev_id)
|
||||||
u16 restart_flag = 0;
|
u16 restart_flag = 0;
|
||||||
u16 intr_stat;
|
u16 intr_stat;
|
||||||
|
|
||||||
if (i2c->dev_comp->auto_restart)
|
if (i2c->auto_restart)
|
||||||
restart_flag = I2C_RS_TRANSFER;
|
restart_flag = I2C_RS_TRANSFER;
|
||||||
|
|
||||||
intr_stat = readw(i2c->base + OFFSET_INTR_STAT);
|
intr_stat = readw(i2c->base + OFFSET_INTR_STAT);
|
||||||
|
@ -571,8 +601,16 @@ static irqreturn_t mtk_i2c_irq(int irqno, void *dev_id)
|
||||||
* i2c->irq_stat need keep the two interrupt value.
|
* i2c->irq_stat need keep the two interrupt value.
|
||||||
*/
|
*/
|
||||||
i2c->irq_stat |= intr_stat;
|
i2c->irq_stat |= intr_stat;
|
||||||
if (i2c->irq_stat & (I2C_TRANSAC_COMP | restart_flag))
|
|
||||||
complete(&i2c->msg_complete);
|
if (i2c->ignore_restart_irq && (i2c->irq_stat & restart_flag)) {
|
||||||
|
i2c->ignore_restart_irq = false;
|
||||||
|
i2c->irq_stat = 0;
|
||||||
|
writew(I2C_RS_MUL_CNFG | I2C_RS_MUL_TRIG | I2C_TRANSAC_START,
|
||||||
|
i2c->base + OFFSET_START);
|
||||||
|
} else {
|
||||||
|
if (i2c->irq_stat & (I2C_TRANSAC_COMP | restart_flag))
|
||||||
|
complete(&i2c->msg_complete);
|
||||||
|
}
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,9 @@
|
||||||
|
|
||||||
Note: we assume there can only be one device, with one or more
|
Note: we assume there can only be one device, with one or more
|
||||||
SMBus interfaces.
|
SMBus interfaces.
|
||||||
|
The device can register multiple i2c_adapters (up to PIIX4_MAX_ADAPTERS).
|
||||||
|
For devices supporting multiple ports the i2c_adapter should provide
|
||||||
|
an i2c_algorithm to access them.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
@ -37,6 +40,7 @@
|
||||||
#include <linux/dmi.h>
|
#include <linux/dmi.h>
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
|
||||||
|
|
||||||
/* PIIX4 SMBus address offsets */
|
/* PIIX4 SMBus address offsets */
|
||||||
|
@ -75,6 +79,16 @@
|
||||||
#define PIIX4_WORD_DATA 0x0C
|
#define PIIX4_WORD_DATA 0x0C
|
||||||
#define PIIX4_BLOCK_DATA 0x14
|
#define PIIX4_BLOCK_DATA 0x14
|
||||||
|
|
||||||
|
/* Multi-port constants */
|
||||||
|
#define PIIX4_MAX_ADAPTERS 4
|
||||||
|
|
||||||
|
/* SB800 constants */
|
||||||
|
#define SB800_PIIX4_SMB_IDX 0xcd6
|
||||||
|
|
||||||
|
/* SB800 port is selected by bits 2:1 of the smb_en register (0x2c) */
|
||||||
|
#define SB800_PIIX4_PORT_IDX 0x2c
|
||||||
|
#define SB800_PIIX4_PORT_IDX_MASK 0x06
|
||||||
|
|
||||||
/* insmod parameters */
|
/* insmod parameters */
|
||||||
|
|
||||||
/* If force is set to anything different from 0, we forcibly enable the
|
/* If force is set to anything different from 0, we forcibly enable the
|
||||||
|
@ -122,8 +136,19 @@ static const struct dmi_system_id piix4_dmi_ibm[] = {
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* SB800 globals */
|
||||||
|
static const char *piix4_main_port_names_sb800[PIIX4_MAX_ADAPTERS] = {
|
||||||
|
"SDA0", "SDA2", "SDA3", "SDA4"
|
||||||
|
};
|
||||||
|
static const char *piix4_aux_port_name_sb800 = "SDA1";
|
||||||
|
|
||||||
struct i2c_piix4_adapdata {
|
struct i2c_piix4_adapdata {
|
||||||
unsigned short smba;
|
unsigned short smba;
|
||||||
|
|
||||||
|
/* SB800 */
|
||||||
|
bool sb800_main;
|
||||||
|
unsigned short port;
|
||||||
|
struct mutex *mutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int piix4_setup(struct pci_dev *PIIX4_dev,
|
static int piix4_setup(struct pci_dev *PIIX4_dev,
|
||||||
|
@ -229,7 +254,6 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
|
||||||
const struct pci_device_id *id, u8 aux)
|
const struct pci_device_id *id, u8 aux)
|
||||||
{
|
{
|
||||||
unsigned short piix4_smba;
|
unsigned short piix4_smba;
|
||||||
unsigned short smba_idx = 0xcd6;
|
|
||||||
u8 smba_en_lo, smba_en_hi, smb_en, smb_en_status;
|
u8 smba_en_lo, smba_en_hi, smb_en, smb_en_status;
|
||||||
u8 i2ccfg, i2ccfg_offset = 0x10;
|
u8 i2ccfg, i2ccfg_offset = 0x10;
|
||||||
|
|
||||||
|
@ -251,16 +275,10 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
|
||||||
else
|
else
|
||||||
smb_en = (aux) ? 0x28 : 0x2c;
|
smb_en = (aux) ? 0x28 : 0x2c;
|
||||||
|
|
||||||
if (!request_region(smba_idx, 2, "smba_idx")) {
|
outb_p(smb_en, SB800_PIIX4_SMB_IDX);
|
||||||
dev_err(&PIIX4_dev->dev, "SMBus base address index region "
|
smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1);
|
||||||
"0x%x already in use!\n", smba_idx);
|
outb_p(smb_en + 1, SB800_PIIX4_SMB_IDX);
|
||||||
return -EBUSY;
|
smba_en_hi = inb_p(SB800_PIIX4_SMB_IDX + 1);
|
||||||
}
|
|
||||||
outb_p(smb_en, smba_idx);
|
|
||||||
smba_en_lo = inb_p(smba_idx + 1);
|
|
||||||
outb_p(smb_en + 1, smba_idx);
|
|
||||||
smba_en_hi = inb_p(smba_idx + 1);
|
|
||||||
release_region(smba_idx, 2);
|
|
||||||
|
|
||||||
if (!smb_en) {
|
if (!smb_en) {
|
||||||
smb_en_status = smba_en_lo & 0x10;
|
smb_en_status = smba_en_lo & 0x10;
|
||||||
|
@ -483,7 +501,7 @@ static s32 piix4_access(struct i2c_adapter * adap, u16 addr,
|
||||||
if (len == 0 || len > I2C_SMBUS_BLOCK_MAX)
|
if (len == 0 || len > I2C_SMBUS_BLOCK_MAX)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
outb_p(len, SMBHSTDAT0);
|
outb_p(len, SMBHSTDAT0);
|
||||||
i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */
|
inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */
|
||||||
for (i = 1; i <= len; i++)
|
for (i = 1; i <= len; i++)
|
||||||
outb_p(data->block[i], SMBBLKDAT);
|
outb_p(data->block[i], SMBBLKDAT);
|
||||||
}
|
}
|
||||||
|
@ -516,7 +534,7 @@ static s32 piix4_access(struct i2c_adapter * adap, u16 addr,
|
||||||
data->block[0] = inb_p(SMBHSTDAT0);
|
data->block[0] = inb_p(SMBHSTDAT0);
|
||||||
if (data->block[0] == 0 || data->block[0] > I2C_SMBUS_BLOCK_MAX)
|
if (data->block[0] == 0 || data->block[0] > I2C_SMBUS_BLOCK_MAX)
|
||||||
return -EPROTO;
|
return -EPROTO;
|
||||||
i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */
|
inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */
|
||||||
for (i = 1; i <= data->block[0]; i++)
|
for (i = 1; i <= data->block[0]; i++)
|
||||||
data->block[i] = inb_p(SMBBLKDAT);
|
data->block[i] = inb_p(SMBBLKDAT);
|
||||||
break;
|
break;
|
||||||
|
@ -524,6 +542,43 @@ static s32 piix4_access(struct i2c_adapter * adap, u16 addr,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handles access to multiple SMBus ports on the SB800.
|
||||||
|
* The port is selected by bits 2:1 of the smb_en register (0x2c).
|
||||||
|
* Returns negative errno on error.
|
||||||
|
*
|
||||||
|
* Note: The selected port must be returned to the initial selection to avoid
|
||||||
|
* problems on certain systems.
|
||||||
|
*/
|
||||||
|
static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr,
|
||||||
|
unsigned short flags, char read_write,
|
||||||
|
u8 command, int size, union i2c_smbus_data *data)
|
||||||
|
{
|
||||||
|
struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(adap);
|
||||||
|
u8 smba_en_lo;
|
||||||
|
u8 port;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
mutex_lock(adapdata->mutex);
|
||||||
|
|
||||||
|
outb_p(SB800_PIIX4_PORT_IDX, SB800_PIIX4_SMB_IDX);
|
||||||
|
smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1);
|
||||||
|
|
||||||
|
port = adapdata->port;
|
||||||
|
if ((smba_en_lo & SB800_PIIX4_PORT_IDX_MASK) != (port << 1))
|
||||||
|
outb_p((smba_en_lo & ~SB800_PIIX4_PORT_IDX_MASK) | (port << 1),
|
||||||
|
SB800_PIIX4_SMB_IDX + 1);
|
||||||
|
|
||||||
|
retval = piix4_access(adap, addr, flags, read_write,
|
||||||
|
command, size, data);
|
||||||
|
|
||||||
|
outb_p(smba_en_lo, SB800_PIIX4_SMB_IDX + 1);
|
||||||
|
|
||||||
|
mutex_unlock(adapdata->mutex);
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
static u32 piix4_func(struct i2c_adapter *adapter)
|
static u32 piix4_func(struct i2c_adapter *adapter)
|
||||||
{
|
{
|
||||||
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
|
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
|
||||||
|
@ -536,6 +591,11 @@ static const struct i2c_algorithm smbus_algorithm = {
|
||||||
.functionality = piix4_func,
|
.functionality = piix4_func,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct i2c_algorithm piix4_smbus_algorithm_sb800 = {
|
||||||
|
.smbus_xfer = piix4_access_sb800,
|
||||||
|
.functionality = piix4_func,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct pci_device_id piix4_ids[] = {
|
static const struct pci_device_id piix4_ids[] = {
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3) },
|
||||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3) },
|
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3) },
|
||||||
|
@ -561,11 +621,11 @@ static const struct pci_device_id piix4_ids[] = {
|
||||||
|
|
||||||
MODULE_DEVICE_TABLE (pci, piix4_ids);
|
MODULE_DEVICE_TABLE (pci, piix4_ids);
|
||||||
|
|
||||||
static struct i2c_adapter *piix4_main_adapter;
|
static struct i2c_adapter *piix4_main_adapters[PIIX4_MAX_ADAPTERS];
|
||||||
static struct i2c_adapter *piix4_aux_adapter;
|
static struct i2c_adapter *piix4_aux_adapter;
|
||||||
|
|
||||||
static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba,
|
static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba,
|
||||||
struct i2c_adapter **padap)
|
const char *name, struct i2c_adapter **padap)
|
||||||
{
|
{
|
||||||
struct i2c_adapter *adap;
|
struct i2c_adapter *adap;
|
||||||
struct i2c_piix4_adapdata *adapdata;
|
struct i2c_piix4_adapdata *adapdata;
|
||||||
|
@ -594,7 +654,7 @@ static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba,
|
||||||
adap->dev.parent = &dev->dev;
|
adap->dev.parent = &dev->dev;
|
||||||
|
|
||||||
snprintf(adap->name, sizeof(adap->name),
|
snprintf(adap->name, sizeof(adap->name),
|
||||||
"SMBus PIIX4 adapter at %04x", smba);
|
"SMBus PIIX4 adapter %s at %04x", name, smba);
|
||||||
|
|
||||||
i2c_set_adapdata(adap, adapdata);
|
i2c_set_adapdata(adap, adapdata);
|
||||||
|
|
||||||
|
@ -611,6 +671,54 @@ static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int piix4_add_adapters_sb800(struct pci_dev *dev, unsigned short smba)
|
||||||
|
{
|
||||||
|
struct mutex *mutex;
|
||||||
|
struct i2c_piix4_adapdata *adapdata;
|
||||||
|
int port;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
mutex = kzalloc(sizeof(*mutex), GFP_KERNEL);
|
||||||
|
if (mutex == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
mutex_init(mutex);
|
||||||
|
|
||||||
|
for (port = 0; port < PIIX4_MAX_ADAPTERS; port++) {
|
||||||
|
retval = piix4_add_adapter(dev, smba,
|
||||||
|
piix4_main_port_names_sb800[port],
|
||||||
|
&piix4_main_adapters[port]);
|
||||||
|
if (retval < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
piix4_main_adapters[port]->algo = &piix4_smbus_algorithm_sb800;
|
||||||
|
|
||||||
|
adapdata = i2c_get_adapdata(piix4_main_adapters[port]);
|
||||||
|
adapdata->sb800_main = true;
|
||||||
|
adapdata->port = port;
|
||||||
|
adapdata->mutex = mutex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
error:
|
||||||
|
dev_err(&dev->dev,
|
||||||
|
"Error setting up SB800 adapters. Unregistering!\n");
|
||||||
|
while (--port >= 0) {
|
||||||
|
adapdata = i2c_get_adapdata(piix4_main_adapters[port]);
|
||||||
|
if (adapdata->smba) {
|
||||||
|
i2c_del_adapter(piix4_main_adapters[port]);
|
||||||
|
kfree(adapdata);
|
||||||
|
kfree(piix4_main_adapters[port]);
|
||||||
|
piix4_main_adapters[port] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(mutex);
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
|
@ -618,20 +726,41 @@ static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||||
if ((dev->vendor == PCI_VENDOR_ID_ATI &&
|
if ((dev->vendor == PCI_VENDOR_ID_ATI &&
|
||||||
dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS &&
|
dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS &&
|
||||||
dev->revision >= 0x40) ||
|
dev->revision >= 0x40) ||
|
||||||
dev->vendor == PCI_VENDOR_ID_AMD)
|
dev->vendor == PCI_VENDOR_ID_AMD) {
|
||||||
|
if (!request_region(SB800_PIIX4_SMB_IDX, 2, "smba_idx")) {
|
||||||
|
dev_err(&dev->dev,
|
||||||
|
"SMBus base address index region 0x%x already in use!\n",
|
||||||
|
SB800_PIIX4_SMB_IDX);
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
/* base address location etc changed in SB800 */
|
/* base address location etc changed in SB800 */
|
||||||
retval = piix4_setup_sb800(dev, id, 0);
|
retval = piix4_setup_sb800(dev, id, 0);
|
||||||
else
|
if (retval < 0) {
|
||||||
|
release_region(SB800_PIIX4_SMB_IDX, 2);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try to register multiplexed main SMBus adapter,
|
||||||
|
* give up if we can't
|
||||||
|
*/
|
||||||
|
retval = piix4_add_adapters_sb800(dev, retval);
|
||||||
|
if (retval < 0) {
|
||||||
|
release_region(SB800_PIIX4_SMB_IDX, 2);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
retval = piix4_setup(dev, id);
|
retval = piix4_setup(dev, id);
|
||||||
|
if (retval < 0)
|
||||||
|
return retval;
|
||||||
|
|
||||||
/* If no main SMBus found, give up */
|
/* Try to register main SMBus adapter, give up if we can't */
|
||||||
if (retval < 0)
|
retval = piix4_add_adapter(dev, retval, "main",
|
||||||
return retval;
|
&piix4_main_adapters[0]);
|
||||||
|
if (retval < 0)
|
||||||
/* Try to register main SMBus adapter, give up if we can't */
|
return retval;
|
||||||
retval = piix4_add_adapter(dev, retval, &piix4_main_adapter);
|
}
|
||||||
if (retval < 0)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
/* Check for auxiliary SMBus on some AMD chipsets */
|
/* Check for auxiliary SMBus on some AMD chipsets */
|
||||||
retval = -ENODEV;
|
retval = -ENODEV;
|
||||||
|
@ -654,7 +783,8 @@ static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||||
if (retval > 0) {
|
if (retval > 0) {
|
||||||
/* Try to add the aux adapter if it exists,
|
/* Try to add the aux adapter if it exists,
|
||||||
* piix4_add_adapter will clean up if this fails */
|
* piix4_add_adapter will clean up if this fails */
|
||||||
piix4_add_adapter(dev, retval, &piix4_aux_adapter);
|
piix4_add_adapter(dev, retval, piix4_aux_port_name_sb800,
|
||||||
|
&piix4_aux_adapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -666,7 +796,13 @@ static void piix4_adap_remove(struct i2c_adapter *adap)
|
||||||
|
|
||||||
if (adapdata->smba) {
|
if (adapdata->smba) {
|
||||||
i2c_del_adapter(adap);
|
i2c_del_adapter(adap);
|
||||||
release_region(adapdata->smba, SMBIOSIZE);
|
if (adapdata->port == 0) {
|
||||||
|
release_region(adapdata->smba, SMBIOSIZE);
|
||||||
|
if (adapdata->sb800_main) {
|
||||||
|
kfree(adapdata->mutex);
|
||||||
|
release_region(SB800_PIIX4_SMB_IDX, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
kfree(adapdata);
|
kfree(adapdata);
|
||||||
kfree(adap);
|
kfree(adap);
|
||||||
}
|
}
|
||||||
|
@ -674,9 +810,13 @@ static void piix4_adap_remove(struct i2c_adapter *adap)
|
||||||
|
|
||||||
static void piix4_remove(struct pci_dev *dev)
|
static void piix4_remove(struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
if (piix4_main_adapter) {
|
int port = PIIX4_MAX_ADAPTERS;
|
||||||
piix4_adap_remove(piix4_main_adapter);
|
|
||||||
piix4_main_adapter = NULL;
|
while (--port >= 0) {
|
||||||
|
if (piix4_main_adapters[port]) {
|
||||||
|
piix4_adap_remove(piix4_main_adapters[port]);
|
||||||
|
piix4_main_adapters[port] = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (piix4_aux_adapter) {
|
if (piix4_aux_adapter) {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
/*
|
/*
|
||||||
* Driver for the Renesas RCar I2C unit
|
* Driver for the Renesas RCar I2C unit
|
||||||
*
|
*
|
||||||
* Copyright (C) 2014 Wolfram Sang <wsa@sang-engineering.com>
|
* Copyright (C) 2014-15 Wolfram Sang <wsa@sang-engineering.com>
|
||||||
|
* Copyright (C) 2011-2015 Renesas Electronics Corporation
|
||||||
*
|
*
|
||||||
* Copyright (C) 2012-14 Renesas Solutions Corp.
|
* Copyright (C) 2012-14 Renesas Solutions Corp.
|
||||||
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
|
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
|
||||||
|
@ -9,9 +10,6 @@
|
||||||
* This file is based on the drivers/i2c/busses/i2c-sh7760.c
|
* This file is based on the drivers/i2c/busses/i2c-sh7760.c
|
||||||
* (c) 2005-2008 MSC Vertriebsges.m.b.H, Manuel Lauss <mlau@msc-ge.com>
|
* (c) 2005-2008 MSC Vertriebsges.m.b.H, Manuel Lauss <mlau@msc-ge.com>
|
||||||
*
|
*
|
||||||
* This file used out-of-tree driver i2c-rcar.c
|
|
||||||
* Copyright (C) 2011-2012 Renesas Electronics Corporation
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation; version 2 of the License.
|
* the Free Software Foundation; version 2 of the License.
|
||||||
|
@ -33,7 +31,6 @@
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/spinlock.h>
|
|
||||||
|
|
||||||
/* register offsets */
|
/* register offsets */
|
||||||
#define ICSCR 0x00 /* slave ctrl */
|
#define ICSCR 0x00 /* slave ctrl */
|
||||||
|
@ -84,6 +81,7 @@
|
||||||
|
|
||||||
#define RCAR_BUS_PHASE_START (MDBS | MIE | ESG)
|
#define RCAR_BUS_PHASE_START (MDBS | MIE | ESG)
|
||||||
#define RCAR_BUS_PHASE_DATA (MDBS | MIE)
|
#define RCAR_BUS_PHASE_DATA (MDBS | MIE)
|
||||||
|
#define RCAR_BUS_MASK_DATA (~(ESG | FSB) & 0xFF)
|
||||||
#define RCAR_BUS_PHASE_STOP (MDBS | MIE | FSB)
|
#define RCAR_BUS_PHASE_STOP (MDBS | MIE | FSB)
|
||||||
|
|
||||||
#define RCAR_IRQ_SEND (MNR | MAL | MST | MAT | MDE)
|
#define RCAR_IRQ_SEND (MNR | MAL | MST | MAT | MDE)
|
||||||
|
@ -94,10 +92,13 @@
|
||||||
#define RCAR_IRQ_ACK_RECV (~(MAT | MDR) & 0xFF)
|
#define RCAR_IRQ_ACK_RECV (~(MAT | MDR) & 0xFF)
|
||||||
|
|
||||||
#define ID_LAST_MSG (1 << 0)
|
#define ID_LAST_MSG (1 << 0)
|
||||||
#define ID_IOERROR (1 << 1)
|
#define ID_FIRST_MSG (1 << 1)
|
||||||
#define ID_DONE (1 << 2)
|
#define ID_DONE (1 << 2)
|
||||||
#define ID_ARBLOST (1 << 3)
|
#define ID_ARBLOST (1 << 3)
|
||||||
#define ID_NACK (1 << 4)
|
#define ID_NACK (1 << 4)
|
||||||
|
/* persistent flags */
|
||||||
|
#define ID_P_PM_BLOCKED (1 << 31)
|
||||||
|
#define ID_P_MASK ID_P_PM_BLOCKED
|
||||||
|
|
||||||
enum rcar_i2c_type {
|
enum rcar_i2c_type {
|
||||||
I2C_RCAR_GEN1,
|
I2C_RCAR_GEN1,
|
||||||
|
@ -108,10 +109,10 @@ enum rcar_i2c_type {
|
||||||
struct rcar_i2c_priv {
|
struct rcar_i2c_priv {
|
||||||
void __iomem *io;
|
void __iomem *io;
|
||||||
struct i2c_adapter adap;
|
struct i2c_adapter adap;
|
||||||
struct i2c_msg *msg;
|
struct i2c_msg *msg;
|
||||||
|
int msgs_left;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
|
|
||||||
spinlock_t lock;
|
|
||||||
wait_queue_head_t wait;
|
wait_queue_head_t wait;
|
||||||
|
|
||||||
int pos;
|
int pos;
|
||||||
|
@ -124,9 +125,6 @@ struct rcar_i2c_priv {
|
||||||
#define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent)
|
#define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent)
|
||||||
#define rcar_i2c_is_recv(p) ((p)->msg->flags & I2C_M_RD)
|
#define rcar_i2c_is_recv(p) ((p)->msg->flags & I2C_M_RD)
|
||||||
|
|
||||||
#define rcar_i2c_flags_set(p, f) ((p)->flags |= (f))
|
|
||||||
#define rcar_i2c_flags_has(p, f) ((p)->flags & (f))
|
|
||||||
|
|
||||||
#define LOOP_TIMEOUT 1024
|
#define LOOP_TIMEOUT 1024
|
||||||
|
|
||||||
|
|
||||||
|
@ -144,9 +142,10 @@ static void rcar_i2c_init(struct rcar_i2c_priv *priv)
|
||||||
{
|
{
|
||||||
/* reset master mode */
|
/* reset master mode */
|
||||||
rcar_i2c_write(priv, ICMIER, 0);
|
rcar_i2c_write(priv, ICMIER, 0);
|
||||||
rcar_i2c_write(priv, ICMCR, 0);
|
rcar_i2c_write(priv, ICMCR, MDBS);
|
||||||
rcar_i2c_write(priv, ICMSR, 0);
|
rcar_i2c_write(priv, ICMSR, 0);
|
||||||
rcar_i2c_write(priv, ICMAR, 0);
|
/* start clock */
|
||||||
|
rcar_i2c_write(priv, ICCCR, priv->icccr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv)
|
static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv)
|
||||||
|
@ -163,15 +162,17 @@ static int rcar_i2c_bus_barrier(struct rcar_i2c_priv *priv)
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv,
|
static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv, struct i2c_timings *t)
|
||||||
u32 bus_speed,
|
|
||||||
struct device *dev)
|
|
||||||
{
|
{
|
||||||
u32 scgd, cdf;
|
u32 scgd, cdf, round, ick, sum, scl, cdf_width;
|
||||||
u32 round, ick;
|
|
||||||
u32 scl;
|
|
||||||
u32 cdf_width;
|
|
||||||
unsigned long rate;
|
unsigned long rate;
|
||||||
|
struct device *dev = rcar_i2c_priv_to_dev(priv);
|
||||||
|
|
||||||
|
/* Fall back to previously used values if not supplied */
|
||||||
|
t->bus_freq_hz = t->bus_freq_hz ?: 100000;
|
||||||
|
t->scl_fall_ns = t->scl_fall_ns ?: 35;
|
||||||
|
t->scl_rise_ns = t->scl_rise_ns ?: 200;
|
||||||
|
t->scl_int_delay_ns = t->scl_int_delay_ns ?: 50;
|
||||||
|
|
||||||
switch (priv->devtype) {
|
switch (priv->devtype) {
|
||||||
case I2C_RCAR_GEN1:
|
case I2C_RCAR_GEN1:
|
||||||
|
@ -195,9 +196,9 @@ static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv,
|
||||||
* SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
|
* SCL = ick / (20 + SCGD * 8 + F[(ticf + tr + intd) * ick])
|
||||||
*
|
*
|
||||||
* ick : I2C internal clock < 20 MHz
|
* ick : I2C internal clock < 20 MHz
|
||||||
* ticf : I2C SCL falling time = 35 ns here
|
* ticf : I2C SCL falling time
|
||||||
* tr : I2C SCL rising time = 200 ns here
|
* tr : I2C SCL rising time
|
||||||
* intd : LSI internal delay = 50 ns here
|
* intd : LSI internal delay
|
||||||
* clkp : peripheral_clk
|
* clkp : peripheral_clk
|
||||||
* F[] : integer up-valuation
|
* F[] : integer up-valuation
|
||||||
*/
|
*/
|
||||||
|
@ -213,12 +214,12 @@ static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv,
|
||||||
* it is impossible to calculate large scale
|
* it is impossible to calculate large scale
|
||||||
* number on u32. separate it
|
* number on u32. separate it
|
||||||
*
|
*
|
||||||
* F[(ticf + tr + intd) * ick]
|
* F[(ticf + tr + intd) * ick] with sum = (ticf + tr + intd)
|
||||||
* = F[(35 + 200 + 50)ns * ick]
|
* = F[sum * ick / 1000000000]
|
||||||
* = F[285 * ick / 1000000000]
|
* = F[(ick / 1000000) * sum / 1000]
|
||||||
* = F[(ick / 1000000) * 285 / 1000]
|
|
||||||
*/
|
*/
|
||||||
round = (ick + 500000) / 1000000 * 285;
|
sum = t->scl_fall_ns + t->scl_rise_ns + t->scl_int_delay_ns;
|
||||||
|
round = (ick + 500000) / 1000000 * sum;
|
||||||
round = (round + 500) / 1000;
|
round = (round + 500) / 1000;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -235,7 +236,7 @@ static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv,
|
||||||
*/
|
*/
|
||||||
for (scgd = 0; scgd < 0x40; scgd++) {
|
for (scgd = 0; scgd < 0x40; scgd++) {
|
||||||
scl = ick / (20 + (scgd * 8) + round);
|
scl = ick / (20 + (scgd * 8) + round);
|
||||||
if (scl <= bus_speed)
|
if (scl <= t->bus_freq_hz)
|
||||||
goto scgd_find;
|
goto scgd_find;
|
||||||
}
|
}
|
||||||
dev_err(dev, "it is impossible to calculate best SCL\n");
|
dev_err(dev, "it is impossible to calculate best SCL\n");
|
||||||
|
@ -243,11 +244,9 @@ static int rcar_i2c_clock_calculate(struct rcar_i2c_priv *priv,
|
||||||
|
|
||||||
scgd_find:
|
scgd_find:
|
||||||
dev_dbg(dev, "clk %d/%d(%lu), round %u, CDF:0x%x, SCGD: 0x%x\n",
|
dev_dbg(dev, "clk %d/%d(%lu), round %u, CDF:0x%x, SCGD: 0x%x\n",
|
||||||
scl, bus_speed, clk_get_rate(priv->clk), round, cdf, scgd);
|
scl, t->bus_freq_hz, clk_get_rate(priv->clk), round, cdf, scgd);
|
||||||
|
|
||||||
/*
|
/* keep icccr value */
|
||||||
* keep icccr value
|
|
||||||
*/
|
|
||||||
priv->icccr = scgd << cdf_width | cdf;
|
priv->icccr = scgd << cdf_width | cdf;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -257,33 +256,44 @@ static void rcar_i2c_prepare_msg(struct rcar_i2c_priv *priv)
|
||||||
{
|
{
|
||||||
int read = !!rcar_i2c_is_recv(priv);
|
int read = !!rcar_i2c_is_recv(priv);
|
||||||
|
|
||||||
|
priv->pos = 0;
|
||||||
|
if (priv->msgs_left == 1)
|
||||||
|
priv->flags |= ID_LAST_MSG;
|
||||||
|
|
||||||
rcar_i2c_write(priv, ICMAR, (priv->msg->addr << 1) | read);
|
rcar_i2c_write(priv, ICMAR, (priv->msg->addr << 1) | read);
|
||||||
rcar_i2c_write(priv, ICMSR, 0);
|
/*
|
||||||
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START);
|
* We don't have a testcase but the HW engineers say that the write order
|
||||||
|
* of ICMSR and ICMCR depends on whether we issue START or REP_START. Since
|
||||||
|
* it didn't cause a drawback for me, let's rather be safe than sorry.
|
||||||
|
*/
|
||||||
|
if (priv->flags & ID_FIRST_MSG) {
|
||||||
|
rcar_i2c_write(priv, ICMSR, 0);
|
||||||
|
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_START);
|
||||||
|
} else {
|
||||||
|
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);
|
rcar_i2c_write(priv, ICMIER, read ? RCAR_IRQ_RECV : RCAR_IRQ_SEND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void rcar_i2c_next_msg(struct rcar_i2c_priv *priv)
|
||||||
|
{
|
||||||
|
priv->msg++;
|
||||||
|
priv->msgs_left--;
|
||||||
|
priv->flags &= ID_P_MASK;
|
||||||
|
rcar_i2c_prepare_msg(priv);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* interrupt functions
|
* interrupt functions
|
||||||
*/
|
*/
|
||||||
static int rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
|
static void rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
|
||||||
{
|
{
|
||||||
struct i2c_msg *msg = priv->msg;
|
struct i2c_msg *msg = priv->msg;
|
||||||
|
|
||||||
/*
|
/* FIXME: sometimes, unknown interrupt happened. Do nothing */
|
||||||
* FIXME
|
|
||||||
* sometimes, unknown interrupt happened.
|
|
||||||
* Do nothing
|
|
||||||
*/
|
|
||||||
if (!(msr & MDE))
|
if (!(msr & MDE))
|
||||||
return 0;
|
return;
|
||||||
|
|
||||||
/*
|
|
||||||
* If address transfer phase finished,
|
|
||||||
* goto data phase.
|
|
||||||
*/
|
|
||||||
if (msr & MAT)
|
|
||||||
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_DATA);
|
|
||||||
|
|
||||||
if (priv->pos < msg->len) {
|
if (priv->pos < msg->len) {
|
||||||
/*
|
/*
|
||||||
|
@ -305,67 +315,50 @@ static int rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
|
||||||
* [ICRXTX] -> [SHIFT] -> [I2C bus]
|
* [ICRXTX] -> [SHIFT] -> [I2C bus]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (priv->flags & ID_LAST_MSG)
|
if (priv->flags & ID_LAST_MSG) {
|
||||||
/*
|
/*
|
||||||
* If current msg is the _LAST_ msg,
|
* If current msg is the _LAST_ msg,
|
||||||
* prepare stop condition here.
|
* prepare stop condition here.
|
||||||
* ID_DONE will be set on STOP irq.
|
* ID_DONE will be set on STOP irq.
|
||||||
*/
|
*/
|
||||||
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_STOP);
|
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_STOP);
|
||||||
else
|
} else {
|
||||||
/*
|
rcar_i2c_next_msg(priv);
|
||||||
* If current msg is _NOT_ last msg,
|
return;
|
||||||
* it doesn't call stop phase.
|
}
|
||||||
* thus, there is no STOP irq.
|
|
||||||
* return ID_DONE here.
|
|
||||||
*/
|
|
||||||
return ID_DONE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rcar_i2c_write(priv, ICMSR, RCAR_IRQ_ACK_SEND);
|
rcar_i2c_write(priv, ICMSR, RCAR_IRQ_ACK_SEND);
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr)
|
static void rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr)
|
||||||
{
|
{
|
||||||
struct i2c_msg *msg = priv->msg;
|
struct i2c_msg *msg = priv->msg;
|
||||||
|
|
||||||
/*
|
/* FIXME: sometimes, unknown interrupt happened. Do nothing */
|
||||||
* FIXME
|
|
||||||
* sometimes, unknown interrupt happened.
|
|
||||||
* Do nothing
|
|
||||||
*/
|
|
||||||
if (!(msr & MDR))
|
if (!(msr & MDR))
|
||||||
return 0;
|
return;
|
||||||
|
|
||||||
if (msr & MAT) {
|
if (msr & MAT) {
|
||||||
/*
|
/* Address transfer phase finished, but no data at this point. */
|
||||||
* Address transfer phase finished,
|
|
||||||
* but, there is no data at this point.
|
|
||||||
* Do nothing.
|
|
||||||
*/
|
|
||||||
} else if (priv->pos < msg->len) {
|
} else if (priv->pos < msg->len) {
|
||||||
/*
|
/* get received data */
|
||||||
* get received data
|
|
||||||
*/
|
|
||||||
msg->buf[priv->pos] = rcar_i2c_read(priv, ICRXTX);
|
msg->buf[priv->pos] = rcar_i2c_read(priv, ICRXTX);
|
||||||
priv->pos++;
|
priv->pos++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If next received data is the _LAST_,
|
* If next received data is the _LAST_, go to STOP phase. Might be
|
||||||
* go to STOP phase,
|
* overwritten by REP START when setting up a new msg. Not elegant
|
||||||
* otherwise, go to DATA phase.
|
* but the only stable sequence for REP START I have found so far.
|
||||||
*/
|
*/
|
||||||
if (priv->pos + 1 >= msg->len)
|
if (priv->pos + 1 >= msg->len)
|
||||||
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_STOP);
|
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_STOP);
|
||||||
|
|
||||||
|
if (priv->pos == msg->len && !(priv->flags & ID_LAST_MSG))
|
||||||
|
rcar_i2c_next_msg(priv);
|
||||||
else
|
else
|
||||||
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_DATA);
|
rcar_i2c_write(priv, ICMSR, RCAR_IRQ_ACK_RECV);
|
||||||
|
|
||||||
rcar_i2c_write(priv, ICMSR, RCAR_IRQ_ACK_RECV);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool rcar_i2c_slave_irq(struct rcar_i2c_priv *priv)
|
static bool rcar_i2c_slave_irq(struct rcar_i2c_priv *priv)
|
||||||
|
@ -426,62 +419,57 @@ static bool rcar_i2c_slave_irq(struct rcar_i2c_priv *priv)
|
||||||
static irqreturn_t rcar_i2c_irq(int irq, void *ptr)
|
static irqreturn_t rcar_i2c_irq(int irq, void *ptr)
|
||||||
{
|
{
|
||||||
struct rcar_i2c_priv *priv = ptr;
|
struct rcar_i2c_priv *priv = ptr;
|
||||||
irqreturn_t result = IRQ_HANDLED;
|
u32 msr, val;
|
||||||
u32 msr;
|
|
||||||
|
|
||||||
/*-------------- spin lock -----------------*/
|
/* Clear START or STOP as soon as we can */
|
||||||
spin_lock(&priv->lock);
|
val = rcar_i2c_read(priv, ICMCR);
|
||||||
|
rcar_i2c_write(priv, ICMCR, val & RCAR_BUS_MASK_DATA);
|
||||||
if (rcar_i2c_slave_irq(priv))
|
|
||||||
goto exit;
|
|
||||||
|
|
||||||
msr = rcar_i2c_read(priv, ICMSR);
|
msr = rcar_i2c_read(priv, ICMSR);
|
||||||
|
|
||||||
/* Only handle interrupts that are currently enabled */
|
/* Only handle interrupts that are currently enabled */
|
||||||
msr &= rcar_i2c_read(priv, ICMIER);
|
msr &= rcar_i2c_read(priv, ICMIER);
|
||||||
if (!msr) {
|
if (!msr) {
|
||||||
result = IRQ_NONE;
|
if (rcar_i2c_slave_irq(priv))
|
||||||
goto exit;
|
return IRQ_HANDLED;
|
||||||
|
|
||||||
|
return IRQ_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Arbitration lost */
|
/* Arbitration lost */
|
||||||
if (msr & MAL) {
|
if (msr & MAL) {
|
||||||
rcar_i2c_flags_set(priv, (ID_DONE | ID_ARBLOST));
|
priv->flags |= ID_DONE | ID_ARBLOST;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Nack */
|
/* Nack */
|
||||||
if (msr & MNR) {
|
if (msr & MNR) {
|
||||||
/* go to stop phase */
|
/* HW automatically sends STOP after received NACK */
|
||||||
rcar_i2c_write(priv, ICMCR, RCAR_BUS_PHASE_STOP);
|
|
||||||
rcar_i2c_write(priv, ICMIER, RCAR_IRQ_STOP);
|
rcar_i2c_write(priv, ICMIER, RCAR_IRQ_STOP);
|
||||||
rcar_i2c_flags_set(priv, ID_NACK);
|
priv->flags |= ID_NACK;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Stop */
|
/* Stop */
|
||||||
if (msr & MST) {
|
if (msr & MST) {
|
||||||
rcar_i2c_flags_set(priv, ID_DONE);
|
priv->msgs_left--; /* The last message also made it */
|
||||||
|
priv->flags |= ID_DONE;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rcar_i2c_is_recv(priv))
|
if (rcar_i2c_is_recv(priv))
|
||||||
rcar_i2c_flags_set(priv, rcar_i2c_irq_recv(priv, msr));
|
rcar_i2c_irq_recv(priv, msr);
|
||||||
else
|
else
|
||||||
rcar_i2c_flags_set(priv, rcar_i2c_irq_send(priv, msr));
|
rcar_i2c_irq_send(priv, msr);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
if (rcar_i2c_flags_has(priv, ID_DONE)) {
|
if (priv->flags & ID_DONE) {
|
||||||
rcar_i2c_write(priv, ICMIER, 0);
|
rcar_i2c_write(priv, ICMIER, 0);
|
||||||
rcar_i2c_write(priv, ICMSR, 0);
|
rcar_i2c_write(priv, ICMSR, 0);
|
||||||
wake_up(&priv->wait);
|
wake_up(&priv->wait);
|
||||||
}
|
}
|
||||||
|
|
||||||
exit:
|
return IRQ_HANDLED;
|
||||||
spin_unlock(&priv->lock);
|
|
||||||
/*-------------- spin unlock -----------------*/
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
|
static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
|
||||||
|
@ -490,22 +478,11 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
|
||||||
{
|
{
|
||||||
struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
|
struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
|
||||||
struct device *dev = rcar_i2c_priv_to_dev(priv);
|
struct device *dev = rcar_i2c_priv_to_dev(priv);
|
||||||
unsigned long flags;
|
|
||||||
int i, ret;
|
int i, ret;
|
||||||
long timeout;
|
long time_left;
|
||||||
|
|
||||||
pm_runtime_get_sync(dev);
|
pm_runtime_get_sync(dev);
|
||||||
|
|
||||||
/*-------------- spin lock -----------------*/
|
|
||||||
spin_lock_irqsave(&priv->lock, flags);
|
|
||||||
|
|
||||||
rcar_i2c_init(priv);
|
|
||||||
/* start clock */
|
|
||||||
rcar_i2c_write(priv, ICCCR, priv->icccr);
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&priv->lock, flags);
|
|
||||||
/*-------------- spin unlock -----------------*/
|
|
||||||
|
|
||||||
ret = rcar_i2c_bus_barrier(priv);
|
ret = rcar_i2c_bus_barrier(priv);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -514,48 +491,27 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
|
||||||
/* This HW can't send STOP after address phase */
|
/* This HW can't send STOP after address phase */
|
||||||
if (msgs[i].len == 0) {
|
if (msgs[i].len == 0) {
|
||||||
ret = -EOPNOTSUPP;
|
ret = -EOPNOTSUPP;
|
||||||
break;
|
goto out;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*-------------- spin lock -----------------*/
|
/* init first message */
|
||||||
spin_lock_irqsave(&priv->lock, flags);
|
priv->msg = msgs;
|
||||||
|
priv->msgs_left = num;
|
||||||
|
priv->flags = (priv->flags & ID_P_MASK) | ID_FIRST_MSG;
|
||||||
|
rcar_i2c_prepare_msg(priv);
|
||||||
|
|
||||||
/* init each data */
|
time_left = wait_event_timeout(priv->wait, priv->flags & ID_DONE,
|
||||||
priv->msg = &msgs[i];
|
num * adap->timeout);
|
||||||
priv->pos = 0;
|
if (!time_left) {
|
||||||
priv->flags = 0;
|
rcar_i2c_init(priv);
|
||||||
if (i == num - 1)
|
ret = -ETIMEDOUT;
|
||||||
rcar_i2c_flags_set(priv, ID_LAST_MSG);
|
} else if (priv->flags & ID_NACK) {
|
||||||
|
ret = -ENXIO;
|
||||||
rcar_i2c_prepare_msg(priv);
|
} else if (priv->flags & ID_ARBLOST) {
|
||||||
|
ret = -EAGAIN;
|
||||||
spin_unlock_irqrestore(&priv->lock, flags);
|
} else {
|
||||||
/*-------------- spin unlock -----------------*/
|
ret = num - priv->msgs_left; /* The number of transfer */
|
||||||
|
|
||||||
timeout = wait_event_timeout(priv->wait,
|
|
||||||
rcar_i2c_flags_has(priv, ID_DONE),
|
|
||||||
adap->timeout);
|
|
||||||
if (!timeout) {
|
|
||||||
ret = -ETIMEDOUT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rcar_i2c_flags_has(priv, ID_NACK)) {
|
|
||||||
ret = -ENXIO;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rcar_i2c_flags_has(priv, ID_ARBLOST)) {
|
|
||||||
ret = -EAGAIN;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rcar_i2c_flags_has(priv, ID_IOERROR)) {
|
|
||||||
ret = -EIO;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = i + 1; /* The number of transfer */
|
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
pm_runtime_put(dev);
|
pm_runtime_put(dev);
|
||||||
|
@ -637,7 +593,7 @@ static int rcar_i2c_probe(struct platform_device *pdev)
|
||||||
struct i2c_adapter *adap;
|
struct i2c_adapter *adap;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
u32 bus_speed;
|
struct i2c_timings i2c_t;
|
||||||
int irq, ret;
|
int irq, ret;
|
||||||
|
|
||||||
priv = devm_kzalloc(dev, sizeof(struct rcar_i2c_priv), GFP_KERNEL);
|
priv = devm_kzalloc(dev, sizeof(struct rcar_i2c_priv), GFP_KERNEL);
|
||||||
|
@ -650,23 +606,13 @@ static int rcar_i2c_probe(struct platform_device *pdev)
|
||||||
return PTR_ERR(priv->clk);
|
return PTR_ERR(priv->clk);
|
||||||
}
|
}
|
||||||
|
|
||||||
bus_speed = 100000; /* default 100 kHz */
|
|
||||||
of_property_read_u32(dev->of_node, "clock-frequency", &bus_speed);
|
|
||||||
|
|
||||||
priv->devtype = (enum rcar_i2c_type)of_match_device(rcar_i2c_dt_ids, dev)->data;
|
|
||||||
|
|
||||||
ret = rcar_i2c_clock_calculate(priv, bus_speed, dev);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
priv->io = devm_ioremap_resource(dev, res);
|
priv->io = devm_ioremap_resource(dev, res);
|
||||||
if (IS_ERR(priv->io))
|
if (IS_ERR(priv->io))
|
||||||
return PTR_ERR(priv->io);
|
return PTR_ERR(priv->io);
|
||||||
|
|
||||||
irq = platform_get_irq(pdev, 0);
|
priv->devtype = (enum rcar_i2c_type)of_match_device(rcar_i2c_dt_ids, dev)->data;
|
||||||
init_waitqueue_head(&priv->wait);
|
init_waitqueue_head(&priv->wait);
|
||||||
spin_lock_init(&priv->lock);
|
|
||||||
|
|
||||||
adap = &priv->adap;
|
adap = &priv->adap;
|
||||||
adap->nr = pdev->id;
|
adap->nr = pdev->id;
|
||||||
|
@ -678,26 +624,47 @@ static int rcar_i2c_probe(struct platform_device *pdev)
|
||||||
i2c_set_adapdata(adap, priv);
|
i2c_set_adapdata(adap, priv);
|
||||||
strlcpy(adap->name, pdev->name, sizeof(adap->name));
|
strlcpy(adap->name, pdev->name, sizeof(adap->name));
|
||||||
|
|
||||||
ret = devm_request_irq(dev, irq, rcar_i2c_irq, 0,
|
i2c_parse_fw_timings(dev, &i2c_t, false);
|
||||||
dev_name(dev), priv);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(dev, "cannot get irq %d\n", irq);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
pm_runtime_enable(dev);
|
pm_runtime_enable(dev);
|
||||||
|
pm_runtime_get_sync(dev);
|
||||||
|
ret = rcar_i2c_clock_calculate(priv, &i2c_t);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out_pm_put;
|
||||||
|
|
||||||
|
rcar_i2c_init(priv);
|
||||||
|
|
||||||
|
/* Don't suspend when multi-master to keep arbitration working */
|
||||||
|
if (of_property_read_bool(dev->of_node, "multi-master"))
|
||||||
|
priv->flags |= ID_P_PM_BLOCKED;
|
||||||
|
else
|
||||||
|
pm_runtime_put(dev);
|
||||||
|
|
||||||
|
|
||||||
|
irq = platform_get_irq(pdev, 0);
|
||||||
|
ret = devm_request_irq(dev, irq, rcar_i2c_irq, 0, dev_name(dev), priv);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev, "cannot get irq %d\n", irq);
|
||||||
|
goto out_pm_disable;
|
||||||
|
}
|
||||||
|
|
||||||
platform_set_drvdata(pdev, priv);
|
platform_set_drvdata(pdev, priv);
|
||||||
|
|
||||||
ret = i2c_add_numbered_adapter(adap);
|
ret = i2c_add_numbered_adapter(adap);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(dev, "reg adap failed: %d\n", ret);
|
dev_err(dev, "reg adap failed: %d\n", ret);
|
||||||
pm_runtime_disable(dev);
|
goto out_pm_disable;
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_info(dev, "probed\n");
|
dev_info(dev, "probed\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
out_pm_put:
|
||||||
|
pm_runtime_put(dev);
|
||||||
|
out_pm_disable:
|
||||||
|
pm_runtime_disable(dev);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rcar_i2c_remove(struct platform_device *pdev)
|
static int rcar_i2c_remove(struct platform_device *pdev)
|
||||||
|
@ -706,6 +673,8 @@ static int rcar_i2c_remove(struct platform_device *pdev)
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
|
|
||||||
i2c_del_adapter(&priv->adap);
|
i2c_del_adapter(&priv->adap);
|
||||||
|
if (priv->flags & ID_P_PM_BLOCKED)
|
||||||
|
pm_runtime_put(dev);
|
||||||
pm_runtime_disable(dev);
|
pm_runtime_disable(dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -784,7 +784,6 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
|
||||||
int retry;
|
int retry;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
pm_runtime_get_sync(&adap->dev);
|
|
||||||
ret = clk_enable(i2c->clk);
|
ret = clk_enable(i2c->clk);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -795,7 +794,6 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
|
||||||
|
|
||||||
if (ret != -EAGAIN) {
|
if (ret != -EAGAIN) {
|
||||||
clk_disable(i2c->clk);
|
clk_disable(i2c->clk);
|
||||||
pm_runtime_put(&adap->dev);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -805,7 +803,6 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
|
||||||
}
|
}
|
||||||
|
|
||||||
clk_disable(i2c->clk);
|
clk_disable(i2c->clk);
|
||||||
pm_runtime_put(&adap->dev);
|
|
||||||
return -EREMOTEIO;
|
return -EREMOTEIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1256,8 +1253,6 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
pm_runtime_enable(&i2c->adap.dev);
|
|
||||||
|
|
||||||
dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
|
dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1273,7 +1268,6 @@ static int s3c24xx_i2c_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
clk_unprepare(i2c->clk);
|
clk_unprepare(i2c->clk);
|
||||||
|
|
||||||
pm_runtime_disable(&i2c->adap.dev);
|
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
|
||||||
s3c24xx_i2c_deregister_cpufreq(i2c);
|
s3c24xx_i2c_deregister_cpufreq(i2c);
|
||||||
|
|
|
@ -708,8 +708,7 @@ static int st_i2c_xfer(struct i2c_adapter *i2c_adap,
|
||||||
#ifdef CONFIG_PM_SLEEP
|
#ifdef CONFIG_PM_SLEEP
|
||||||
static int st_i2c_suspend(struct device *dev)
|
static int st_i2c_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct platform_device *pdev =
|
struct platform_device *pdev = to_platform_device(dev);
|
||||||
container_of(dev, struct platform_device, dev);
|
|
||||||
struct st_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
|
struct st_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
if (i2c_dev->busy)
|
if (i2c_dev->busy)
|
||||||
|
|
|
@ -130,7 +130,13 @@ static int taos_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
if (p[0] == 'x') {
|
if (p[0] == 'x') {
|
||||||
data->byte = simple_strtol(p + 1, NULL, 16);
|
/*
|
||||||
|
* Voluntarily dropping error code of kstrtou8 since all
|
||||||
|
* error code that it could return are invalid according
|
||||||
|
* to Documentation/i2c/fault-codes.
|
||||||
|
*/
|
||||||
|
if (kstrtou8(p + 1, 16, &data->byte))
|
||||||
|
return -EPROTO;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -466,6 +466,11 @@ static int uniphier_fi2c_clk_init(struct device *dev,
|
||||||
if (of_property_read_u32(np, "clock-frequency", &bus_speed))
|
if (of_property_read_u32(np, "clock-frequency", &bus_speed))
|
||||||
bus_speed = UNIPHIER_FI2C_DEFAULT_SPEED;
|
bus_speed = UNIPHIER_FI2C_DEFAULT_SPEED;
|
||||||
|
|
||||||
|
if (!bus_speed) {
|
||||||
|
dev_err(dev, "clock-freqyency should not be zero\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
if (bus_speed > UNIPHIER_FI2C_MAX_SPEED)
|
if (bus_speed > UNIPHIER_FI2C_MAX_SPEED)
|
||||||
bus_speed = UNIPHIER_FI2C_MAX_SPEED;
|
bus_speed = UNIPHIER_FI2C_MAX_SPEED;
|
||||||
|
|
||||||
|
@ -481,6 +486,10 @@ static int uniphier_fi2c_clk_init(struct device *dev,
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
clk_rate = clk_get_rate(priv->clk);
|
clk_rate = clk_get_rate(priv->clk);
|
||||||
|
if (!clk_rate) {
|
||||||
|
dev_err(dev, "input clock rate should not be zero\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
uniphier_fi2c_reset(priv);
|
uniphier_fi2c_reset(priv);
|
||||||
|
|
||||||
|
@ -531,7 +540,7 @@ static int uniphier_fi2c_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
ret = uniphier_fi2c_clk_init(dev, priv);
|
ret = uniphier_fi2c_clk_init(dev, priv);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
goto err;
|
||||||
|
|
||||||
ret = devm_request_irq(dev, irq, uniphier_fi2c_interrupt, 0,
|
ret = devm_request_irq(dev, irq, uniphier_fi2c_interrupt, 0,
|
||||||
pdev->name, priv);
|
pdev->name, priv);
|
||||||
|
|
|
@ -327,6 +327,11 @@ static int uniphier_i2c_clk_init(struct device *dev,
|
||||||
if (of_property_read_u32(np, "clock-frequency", &bus_speed))
|
if (of_property_read_u32(np, "clock-frequency", &bus_speed))
|
||||||
bus_speed = UNIPHIER_I2C_DEFAULT_SPEED;
|
bus_speed = UNIPHIER_I2C_DEFAULT_SPEED;
|
||||||
|
|
||||||
|
if (!bus_speed) {
|
||||||
|
dev_err(dev, "clock-freqyency should not be zero\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
if (bus_speed > UNIPHIER_I2C_MAX_SPEED)
|
if (bus_speed > UNIPHIER_I2C_MAX_SPEED)
|
||||||
bus_speed = UNIPHIER_I2C_MAX_SPEED;
|
bus_speed = UNIPHIER_I2C_MAX_SPEED;
|
||||||
|
|
||||||
|
@ -342,6 +347,10 @@ static int uniphier_i2c_clk_init(struct device *dev,
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
clk_rate = clk_get_rate(priv->clk);
|
clk_rate = clk_get_rate(priv->clk);
|
||||||
|
if (!clk_rate) {
|
||||||
|
dev_err(dev, "input clock rate should not be zero\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
uniphier_i2c_reset(priv, true);
|
uniphier_i2c_reset(priv, true);
|
||||||
|
|
||||||
|
@ -388,7 +397,7 @@ static int uniphier_i2c_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
ret = uniphier_i2c_clk_init(dev, priv);
|
ret = uniphier_i2c_clk_init(dev, priv);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
goto err;
|
||||||
|
|
||||||
ret = devm_request_irq(dev, irq, uniphier_i2c_interrupt, 0, pdev->name,
|
ret = devm_request_irq(dev, irq, uniphier_i2c_interrupt, 0, pdev->name,
|
||||||
priv);
|
priv);
|
||||||
|
|
|
@ -70,7 +70,7 @@ struct xiic_i2c {
|
||||||
wait_queue_head_t wait;
|
wait_queue_head_t wait;
|
||||||
struct i2c_adapter adap;
|
struct i2c_adapter adap;
|
||||||
struct i2c_msg *tx_msg;
|
struct i2c_msg *tx_msg;
|
||||||
spinlock_t lock;
|
struct mutex lock;
|
||||||
unsigned int tx_pos;
|
unsigned int tx_pos;
|
||||||
unsigned int nmsgs;
|
unsigned int nmsgs;
|
||||||
enum xilinx_i2c_state state;
|
enum xilinx_i2c_state state;
|
||||||
|
@ -369,7 +369,7 @@ static irqreturn_t xiic_process(int irq, void *dev_id)
|
||||||
* To find which interrupts are pending; AND interrupts pending with
|
* To find which interrupts are pending; AND interrupts pending with
|
||||||
* interrupts masked.
|
* interrupts masked.
|
||||||
*/
|
*/
|
||||||
spin_lock(&i2c->lock);
|
mutex_lock(&i2c->lock);
|
||||||
isr = xiic_getreg32(i2c, XIIC_IISR_OFFSET);
|
isr = xiic_getreg32(i2c, XIIC_IISR_OFFSET);
|
||||||
ier = xiic_getreg32(i2c, XIIC_IIER_OFFSET);
|
ier = xiic_getreg32(i2c, XIIC_IIER_OFFSET);
|
||||||
pend = isr & ier;
|
pend = isr & ier;
|
||||||
|
@ -497,7 +497,7 @@ out:
|
||||||
dev_dbg(i2c->adap.dev.parent, "%s clr: 0x%x\n", __func__, clr);
|
dev_dbg(i2c->adap.dev.parent, "%s clr: 0x%x\n", __func__, clr);
|
||||||
|
|
||||||
xiic_setreg32(i2c, XIIC_IISR_OFFSET, clr);
|
xiic_setreg32(i2c, XIIC_IISR_OFFSET, clr);
|
||||||
spin_unlock(&i2c->lock);
|
mutex_unlock(&i2c->lock);
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -662,10 +662,10 @@ static void __xiic_start_xfer(struct xiic_i2c *i2c)
|
||||||
|
|
||||||
static void xiic_start_xfer(struct xiic_i2c *i2c)
|
static void xiic_start_xfer(struct xiic_i2c *i2c)
|
||||||
{
|
{
|
||||||
spin_lock(&i2c->lock);
|
mutex_lock(&i2c->lock);
|
||||||
xiic_reinit(i2c);
|
xiic_reinit(i2c);
|
||||||
__xiic_start_xfer(i2c);
|
__xiic_start_xfer(i2c);
|
||||||
spin_unlock(&i2c->lock);
|
mutex_unlock(&i2c->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int xiic_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
static int xiic_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
||||||
|
@ -745,7 +745,7 @@ static int xiic_i2c_probe(struct platform_device *pdev)
|
||||||
i2c->adap.dev.parent = &pdev->dev;
|
i2c->adap.dev.parent = &pdev->dev;
|
||||||
i2c->adap.dev.of_node = pdev->dev.of_node;
|
i2c->adap.dev.of_node = pdev->dev.of_node;
|
||||||
|
|
||||||
spin_lock_init(&i2c->lock);
|
mutex_init(&i2c->lock);
|
||||||
init_waitqueue_head(&i2c->wait);
|
init_waitqueue_head(&i2c->wait);
|
||||||
|
|
||||||
ret = devm_request_threaded_irq(&pdev->dev, irq, xiic_isr,
|
ret = devm_request_threaded_irq(&pdev->dev, irq, xiic_isr,
|
||||||
|
|
|
@ -17,6 +17,10 @@
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/wait.h>
|
||||||
|
|
||||||
/* XLR I2C REGISTERS */
|
/* XLR I2C REGISTERS */
|
||||||
#define XLR_I2C_CFG 0x00
|
#define XLR_I2C_CFG 0x00
|
||||||
|
@ -30,6 +34,10 @@
|
||||||
#define XLR_I2C_BYTECNT 0x08
|
#define XLR_I2C_BYTECNT 0x08
|
||||||
#define XLR_I2C_HDSTATIM 0x09
|
#define XLR_I2C_HDSTATIM 0x09
|
||||||
|
|
||||||
|
/* Sigma Designs additional registers */
|
||||||
|
#define XLR_I2C_INT_EN 0x09
|
||||||
|
#define XLR_I2C_INT_STAT 0x0a
|
||||||
|
|
||||||
/* XLR I2C REGISTERS FLAGS */
|
/* XLR I2C REGISTERS FLAGS */
|
||||||
#define XLR_I2C_BUS_BUSY 0x01
|
#define XLR_I2C_BUS_BUSY 0x01
|
||||||
#define XLR_I2C_SDOEMPTY 0x02
|
#define XLR_I2C_SDOEMPTY 0x02
|
||||||
|
@ -63,11 +71,98 @@ static inline u32 xlr_i2c_rdreg(u32 __iomem *base, unsigned int reg)
|
||||||
return __raw_readl(base + reg);
|
return __raw_readl(base + reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define XLR_I2C_FLAG_IRQ 1
|
||||||
|
|
||||||
|
struct xlr_i2c_config {
|
||||||
|
u32 flags; /* optional feature support */
|
||||||
|
u32 status_busy; /* value of STATUS[0] when busy */
|
||||||
|
u32 cfg_extra; /* extra CFG bits to set */
|
||||||
|
};
|
||||||
|
|
||||||
struct xlr_i2c_private {
|
struct xlr_i2c_private {
|
||||||
struct i2c_adapter adap;
|
struct i2c_adapter adap;
|
||||||
u32 __iomem *iobase;
|
u32 __iomem *iobase;
|
||||||
|
int irq;
|
||||||
|
int pos;
|
||||||
|
struct i2c_msg *msg;
|
||||||
|
const struct xlr_i2c_config *cfg;
|
||||||
|
wait_queue_head_t wait;
|
||||||
|
struct clk *clk;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int xlr_i2c_busy(struct xlr_i2c_private *priv, u32 status)
|
||||||
|
{
|
||||||
|
return (status & XLR_I2C_BUS_BUSY) == priv->cfg->status_busy;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xlr_i2c_idle(struct xlr_i2c_private *priv)
|
||||||
|
{
|
||||||
|
return !xlr_i2c_busy(priv, xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xlr_i2c_wait(struct xlr_i2c_private *priv, unsigned long timeout)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
int t;
|
||||||
|
|
||||||
|
t = wait_event_timeout(priv->wait, xlr_i2c_idle(priv),
|
||||||
|
msecs_to_jiffies(timeout));
|
||||||
|
if (!t)
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
|
||||||
|
status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
|
||||||
|
|
||||||
|
return status & XLR_I2C_ACK_ERR ? -EIO : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xlr_i2c_tx_irq(struct xlr_i2c_private *priv, u32 status)
|
||||||
|
{
|
||||||
|
struct i2c_msg *msg = priv->msg;
|
||||||
|
|
||||||
|
if (status & XLR_I2C_SDOEMPTY)
|
||||||
|
xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT,
|
||||||
|
msg->buf[priv->pos++]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xlr_i2c_rx_irq(struct xlr_i2c_private *priv, u32 status)
|
||||||
|
{
|
||||||
|
struct i2c_msg *msg = priv->msg;
|
||||||
|
|
||||||
|
if (status & XLR_I2C_RXRDY)
|
||||||
|
msg->buf[priv->pos++] =
|
||||||
|
xlr_i2c_rdreg(priv->iobase, XLR_I2C_DATAIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t xlr_i2c_irq(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct xlr_i2c_private *priv = dev_id;
|
||||||
|
struct i2c_msg *msg = priv->msg;
|
||||||
|
u32 int_stat, status;
|
||||||
|
|
||||||
|
int_stat = xlr_i2c_rdreg(priv->iobase, XLR_I2C_INT_STAT);
|
||||||
|
if (!int_stat)
|
||||||
|
return IRQ_NONE;
|
||||||
|
|
||||||
|
xlr_i2c_wreg(priv->iobase, XLR_I2C_INT_STAT, int_stat);
|
||||||
|
|
||||||
|
if (!msg)
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
|
||||||
|
status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
|
||||||
|
|
||||||
|
if (priv->pos < msg->len) {
|
||||||
|
if (msg->flags & I2C_M_RD)
|
||||||
|
xlr_i2c_rx_irq(priv, status);
|
||||||
|
else
|
||||||
|
xlr_i2c_tx_irq(priv, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!xlr_i2c_busy(priv, status))
|
||||||
|
wake_up(&priv->wait);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
static int xlr_i2c_tx(struct xlr_i2c_private *priv, u16 len,
|
static int xlr_i2c_tx(struct xlr_i2c_private *priv, u16 len,
|
||||||
u8 *buf, u16 addr)
|
u8 *buf, u16 addr)
|
||||||
{
|
{
|
||||||
|
@ -75,37 +170,48 @@ static int xlr_i2c_tx(struct xlr_i2c_private *priv, u16 len,
|
||||||
unsigned long timeout, stoptime, checktime;
|
unsigned long timeout, stoptime, checktime;
|
||||||
u32 i2c_status;
|
u32 i2c_status;
|
||||||
int pos, timedout;
|
int pos, timedout;
|
||||||
u8 offset, byte;
|
u8 offset;
|
||||||
|
u32 xfer;
|
||||||
|
|
||||||
|
if (!len)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
offset = buf[0];
|
offset = buf[0];
|
||||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_ADDR, offset);
|
xlr_i2c_wreg(priv->iobase, XLR_I2C_ADDR, offset);
|
||||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr);
|
xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr);
|
||||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG, XLR_I2C_CFG_ADDR);
|
xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG,
|
||||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1);
|
XLR_I2C_CFG_ADDR | priv->cfg->cfg_extra);
|
||||||
|
|
||||||
timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT);
|
timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT);
|
||||||
stoptime = jiffies + timeout;
|
stoptime = jiffies + timeout;
|
||||||
timedout = 0;
|
timedout = 0;
|
||||||
pos = 1;
|
|
||||||
retry:
|
|
||||||
if (len == 1) {
|
if (len == 1) {
|
||||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR,
|
xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1);
|
||||||
XLR_I2C_STARTXFR_ND);
|
xfer = XLR_I2C_STARTXFR_ND;
|
||||||
|
pos = 1;
|
||||||
} else {
|
} else {
|
||||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, buf[pos]);
|
xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 2);
|
||||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR,
|
xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, buf[1]);
|
||||||
XLR_I2C_STARTXFR_WR);
|
xfer = XLR_I2C_STARTXFR_WR;
|
||||||
|
pos = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
priv->pos = pos;
|
||||||
|
|
||||||
|
retry:
|
||||||
|
/* retry can only happen on the first byte */
|
||||||
|
xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR, xfer);
|
||||||
|
|
||||||
|
if (priv->irq > 0)
|
||||||
|
return xlr_i2c_wait(priv, XLR_I2C_TIMEOUT * len);
|
||||||
|
|
||||||
while (!timedout) {
|
while (!timedout) {
|
||||||
checktime = jiffies;
|
checktime = jiffies;
|
||||||
i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
|
i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
|
||||||
|
|
||||||
if (i2c_status & XLR_I2C_SDOEMPTY) {
|
if ((i2c_status & XLR_I2C_SDOEMPTY) && pos < len) {
|
||||||
pos++;
|
xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, buf[pos++]);
|
||||||
/* need to do a empty dataout after the last byte */
|
|
||||||
byte = (pos < len) ? buf[pos] : 0;
|
|
||||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, byte);
|
|
||||||
|
|
||||||
/* reset timeout on successful xmit */
|
/* reset timeout on successful xmit */
|
||||||
stoptime = jiffies + timeout;
|
stoptime = jiffies + timeout;
|
||||||
|
@ -121,7 +227,7 @@ retry:
|
||||||
if (i2c_status & XLR_I2C_ACK_ERR)
|
if (i2c_status & XLR_I2C_ACK_ERR)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
if ((i2c_status & XLR_I2C_BUS_BUSY) == 0 && pos >= len)
|
if (!xlr_i2c_busy(priv, i2c_status) && pos >= len)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
dev_err(&adap->dev, "I2C transmit timeout\n");
|
dev_err(&adap->dev, "I2C transmit timeout\n");
|
||||||
|
@ -134,12 +240,17 @@ static int xlr_i2c_rx(struct xlr_i2c_private *priv, u16 len, u8 *buf, u16 addr)
|
||||||
u32 i2c_status;
|
u32 i2c_status;
|
||||||
unsigned long timeout, stoptime, checktime;
|
unsigned long timeout, stoptime, checktime;
|
||||||
int nbytes, timedout;
|
int nbytes, timedout;
|
||||||
u8 byte;
|
|
||||||
|
|
||||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG, XLR_I2C_CFG_NOADDR);
|
if (!len)
|
||||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len);
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG,
|
||||||
|
XLR_I2C_CFG_NOADDR | priv->cfg->cfg_extra);
|
||||||
|
xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1);
|
||||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr);
|
xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr);
|
||||||
|
|
||||||
|
priv->pos = 0;
|
||||||
|
|
||||||
timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT);
|
timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT);
|
||||||
stoptime = jiffies + timeout;
|
stoptime = jiffies + timeout;
|
||||||
timedout = 0;
|
timedout = 0;
|
||||||
|
@ -147,18 +258,18 @@ static int xlr_i2c_rx(struct xlr_i2c_private *priv, u16 len, u8 *buf, u16 addr)
|
||||||
retry:
|
retry:
|
||||||
xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR, XLR_I2C_STARTXFR_RD);
|
xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR, XLR_I2C_STARTXFR_RD);
|
||||||
|
|
||||||
|
if (priv->irq > 0)
|
||||||
|
return xlr_i2c_wait(priv, XLR_I2C_TIMEOUT * len);
|
||||||
|
|
||||||
while (!timedout) {
|
while (!timedout) {
|
||||||
checktime = jiffies;
|
checktime = jiffies;
|
||||||
i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
|
i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
|
||||||
if (i2c_status & XLR_I2C_RXRDY) {
|
if (i2c_status & XLR_I2C_RXRDY) {
|
||||||
if (nbytes > len)
|
if (nbytes >= len)
|
||||||
return -EIO; /* should not happen */
|
return -EIO; /* should not happen */
|
||||||
|
|
||||||
/* we need to do a dummy datain when nbytes == len */
|
buf[nbytes++] =
|
||||||
byte = xlr_i2c_rdreg(priv->iobase, XLR_I2C_DATAIN);
|
xlr_i2c_rdreg(priv->iobase, XLR_I2C_DATAIN);
|
||||||
if (nbytes < len)
|
|
||||||
buf[nbytes] = byte;
|
|
||||||
nbytes++;
|
|
||||||
|
|
||||||
/* reset timeout on successful read */
|
/* reset timeout on successful read */
|
||||||
stoptime = jiffies + timeout;
|
stoptime = jiffies + timeout;
|
||||||
|
@ -174,7 +285,7 @@ retry:
|
||||||
if (i2c_status & XLR_I2C_ACK_ERR)
|
if (i2c_status & XLR_I2C_ACK_ERR)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
if ((i2c_status & XLR_I2C_BUS_BUSY) == 0)
|
if (!xlr_i2c_busy(priv, i2c_status))
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,8 +301,17 @@ static int xlr_i2c_xfer(struct i2c_adapter *adap,
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct xlr_i2c_private *priv = i2c_get_adapdata(adap);
|
struct xlr_i2c_private *priv = i2c_get_adapdata(adap);
|
||||||
|
|
||||||
|
ret = clk_enable(priv->clk);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (priv->irq)
|
||||||
|
xlr_i2c_wreg(priv->iobase, XLR_I2C_INT_EN, 0xf);
|
||||||
|
|
||||||
|
|
||||||
for (i = 0; ret == 0 && i < num; i++) {
|
for (i = 0; ret == 0 && i < num; i++) {
|
||||||
msg = &msgs[i];
|
msg = &msgs[i];
|
||||||
|
priv->msg = msg;
|
||||||
if (msg->flags & I2C_M_RD)
|
if (msg->flags & I2C_M_RD)
|
||||||
ret = xlr_i2c_rx(priv, msg->len, &msg->buf[0],
|
ret = xlr_i2c_rx(priv, msg->len, &msg->buf[0],
|
||||||
msg->addr);
|
msg->addr);
|
||||||
|
@ -200,13 +320,19 @@ static int xlr_i2c_xfer(struct i2c_adapter *adap,
|
||||||
msg->addr);
|
msg->addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (priv->irq)
|
||||||
|
xlr_i2c_wreg(priv->iobase, XLR_I2C_INT_EN, 0);
|
||||||
|
|
||||||
|
clk_disable(priv->clk);
|
||||||
|
priv->msg = NULL;
|
||||||
|
|
||||||
return (ret != 0) ? ret : num;
|
return (ret != 0) ? ret : num;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 xlr_func(struct i2c_adapter *adap)
|
static u32 xlr_func(struct i2c_adapter *adap)
|
||||||
{
|
{
|
||||||
/* Emulate SMBUS over I2C */
|
/* Emulate SMBUS over I2C */
|
||||||
return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C;
|
return (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) | I2C_FUNC_I2C;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct i2c_algorithm xlr_i2c_algo = {
|
static struct i2c_algorithm xlr_i2c_algo = {
|
||||||
|
@ -214,22 +340,89 @@ static struct i2c_algorithm xlr_i2c_algo = {
|
||||||
.functionality = xlr_func,
|
.functionality = xlr_func,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct xlr_i2c_config xlr_i2c_config_default = {
|
||||||
|
.status_busy = XLR_I2C_BUS_BUSY,
|
||||||
|
.cfg_extra = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct xlr_i2c_config xlr_i2c_config_tangox = {
|
||||||
|
.flags = XLR_I2C_FLAG_IRQ,
|
||||||
|
.status_busy = 0,
|
||||||
|
.cfg_extra = 1 << 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id xlr_i2c_dt_ids[] = {
|
||||||
|
{
|
||||||
|
.compatible = "sigma,smp8642-i2c",
|
||||||
|
.data = &xlr_i2c_config_tangox,
|
||||||
|
},
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
static int xlr_i2c_probe(struct platform_device *pdev)
|
static int xlr_i2c_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
const struct of_device_id *match;
|
||||||
struct xlr_i2c_private *priv;
|
struct xlr_i2c_private *priv;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
|
struct clk *clk;
|
||||||
|
unsigned long clk_rate;
|
||||||
|
unsigned long clk_div;
|
||||||
|
u32 busfreq;
|
||||||
|
int irq;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||||
if (!priv)
|
if (!priv)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
match = of_match_device(xlr_i2c_dt_ids, &pdev->dev);
|
||||||
|
if (match)
|
||||||
|
priv->cfg = match->data;
|
||||||
|
else
|
||||||
|
priv->cfg = &xlr_i2c_config_default;
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
priv->iobase = devm_ioremap_resource(&pdev->dev, res);
|
priv->iobase = devm_ioremap_resource(&pdev->dev, res);
|
||||||
if (IS_ERR(priv->iobase))
|
if (IS_ERR(priv->iobase))
|
||||||
return PTR_ERR(priv->iobase);
|
return PTR_ERR(priv->iobase);
|
||||||
|
|
||||||
|
irq = platform_get_irq(pdev, 0);
|
||||||
|
|
||||||
|
if (irq > 0 && (priv->cfg->flags & XLR_I2C_FLAG_IRQ)) {
|
||||||
|
priv->irq = irq;
|
||||||
|
|
||||||
|
xlr_i2c_wreg(priv->iobase, XLR_I2C_INT_EN, 0);
|
||||||
|
xlr_i2c_wreg(priv->iobase, XLR_I2C_INT_STAT, 0xf);
|
||||||
|
|
||||||
|
ret = devm_request_irq(&pdev->dev, priv->irq, xlr_i2c_irq,
|
||||||
|
IRQF_SHARED, dev_name(&pdev->dev),
|
||||||
|
priv);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
init_waitqueue_head(&priv->wait);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (of_property_read_u32(pdev->dev.of_node, "clock-frequency",
|
||||||
|
&busfreq))
|
||||||
|
busfreq = 100000;
|
||||||
|
|
||||||
|
clk = devm_clk_get(&pdev->dev, NULL);
|
||||||
|
if (!IS_ERR(clk)) {
|
||||||
|
ret = clk_prepare_enable(clk);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
clk_rate = clk_get_rate(clk);
|
||||||
|
clk_div = DIV_ROUND_UP(clk_rate, 2 * busfreq);
|
||||||
|
xlr_i2c_wreg(priv->iobase, XLR_I2C_CLKDIV, clk_div);
|
||||||
|
|
||||||
|
clk_disable(clk);
|
||||||
|
priv->clk = clk;
|
||||||
|
}
|
||||||
|
|
||||||
priv->adap.dev.parent = &pdev->dev;
|
priv->adap.dev.parent = &pdev->dev;
|
||||||
|
priv->adap.dev.of_node = pdev->dev.of_node;
|
||||||
priv->adap.owner = THIS_MODULE;
|
priv->adap.owner = THIS_MODULE;
|
||||||
priv->adap.algo_data = priv;
|
priv->adap.algo_data = priv;
|
||||||
priv->adap.algo = &xlr_i2c_algo;
|
priv->adap.algo = &xlr_i2c_algo;
|
||||||
|
@ -255,6 +448,8 @@ static int xlr_i2c_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
priv = platform_get_drvdata(pdev);
|
priv = platform_get_drvdata(pdev);
|
||||||
i2c_del_adapter(&priv->adap);
|
i2c_del_adapter(&priv->adap);
|
||||||
|
clk_unprepare(priv->clk);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,6 +458,7 @@ static struct platform_driver xlr_i2c_driver = {
|
||||||
.remove = xlr_i2c_remove,
|
.remove = xlr_i2c_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "xlr-i2cbus",
|
.name = "xlr-i2cbus",
|
||||||
|
.of_match_table = xlr_i2c_dt_ids,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,7 @@
|
||||||
#include <linux/jump_label.h>
|
#include <linux/jump_label.h>
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
#include <linux/property.h>
|
||||||
|
|
||||||
#include "i2c-core.h"
|
#include "i2c-core.h"
|
||||||
|
|
||||||
|
@ -1563,6 +1564,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
|
||||||
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
|
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
|
||||||
|
|
||||||
pm_runtime_no_callbacks(&adap->dev);
|
pm_runtime_no_callbacks(&adap->dev);
|
||||||
|
pm_runtime_enable(&adap->dev);
|
||||||
|
|
||||||
#ifdef CONFIG_I2C_COMPAT
|
#ifdef CONFIG_I2C_COMPAT
|
||||||
res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,
|
res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,
|
||||||
|
@ -1817,6 +1819,8 @@ void i2c_del_adapter(struct i2c_adapter *adap)
|
||||||
/* device name is gone after device_unregister */
|
/* device name is gone after device_unregister */
|
||||||
dev_dbg(&adap->dev, "adapter [%s] unregistered\n", adap->name);
|
dev_dbg(&adap->dev, "adapter [%s] unregistered\n", adap->name);
|
||||||
|
|
||||||
|
pm_runtime_disable(&adap->dev);
|
||||||
|
|
||||||
/* wait until all references to the device are gone
|
/* wait until all references to the device are gone
|
||||||
*
|
*
|
||||||
* FIXME: This is old code and should ideally be replaced by an
|
* FIXME: This is old code and should ideally be replaced by an
|
||||||
|
@ -1839,6 +1843,58 @@ void i2c_del_adapter(struct i2c_adapter *adap)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(i2c_del_adapter);
|
EXPORT_SYMBOL(i2c_del_adapter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* i2c_parse_fw_timings - get I2C related timing parameters from firmware
|
||||||
|
* @dev: The device to scan for I2C timing properties
|
||||||
|
* @t: the i2c_timings struct to be filled with values
|
||||||
|
* @use_defaults: bool to use sane defaults derived from the I2C specification
|
||||||
|
* when properties are not found, otherwise use 0
|
||||||
|
*
|
||||||
|
* Scan the device for the generic I2C properties describing timing parameters
|
||||||
|
* for the signal and fill the given struct with the results. If a property was
|
||||||
|
* not found and use_defaults was true, then maximum timings are assumed which
|
||||||
|
* are derived from the I2C specification. If use_defaults is not used, the
|
||||||
|
* results will be 0, so drivers can apply their own defaults later. The latter
|
||||||
|
* is mainly intended for avoiding regressions of existing drivers which want
|
||||||
|
* to switch to this function. New drivers almost always should use the defaults.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void i2c_parse_fw_timings(struct device *dev, struct i2c_timings *t, bool use_defaults)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
memset(t, 0, sizeof(*t));
|
||||||
|
|
||||||
|
ret = device_property_read_u32(dev, "clock-frequency", &t->bus_freq_hz);
|
||||||
|
if (ret && use_defaults)
|
||||||
|
t->bus_freq_hz = 100000;
|
||||||
|
|
||||||
|
ret = device_property_read_u32(dev, "i2c-scl-rising-time-ns", &t->scl_rise_ns);
|
||||||
|
if (ret && use_defaults) {
|
||||||
|
if (t->bus_freq_hz <= 100000)
|
||||||
|
t->scl_rise_ns = 1000;
|
||||||
|
else if (t->bus_freq_hz <= 400000)
|
||||||
|
t->scl_rise_ns = 300;
|
||||||
|
else
|
||||||
|
t->scl_rise_ns = 120;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = device_property_read_u32(dev, "i2c-scl-falling-time-ns", &t->scl_fall_ns);
|
||||||
|
if (ret && use_defaults) {
|
||||||
|
if (t->bus_freq_hz <= 400000)
|
||||||
|
t->scl_fall_ns = 300;
|
||||||
|
else
|
||||||
|
t->scl_fall_ns = 120;
|
||||||
|
}
|
||||||
|
|
||||||
|
device_property_read_u32(dev, "i2c-scl-internal-delay-ns", &t->scl_int_delay_ns);
|
||||||
|
|
||||||
|
ret = device_property_read_u32(dev, "i2c-sda-falling-time-ns", &t->sda_fall_ns);
|
||||||
|
if (ret && use_defaults)
|
||||||
|
t->sda_fall_ns = t->scl_fall_ns;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(i2c_parse_fw_timings);
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
int i2c_for_each_dev(void *data, int (*fn)(struct device *, void *))
|
int i2c_for_each_dev(void *data, int (*fn)(struct device *, void *))
|
||||||
|
|
|
@ -413,6 +413,22 @@ struct i2c_algorithm {
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct i2c_timings - I2C timing information
|
||||||
|
* @bus_freq_hz: the bus frequency in Hz
|
||||||
|
* @scl_rise_ns: time SCL signal takes to rise in ns; t(r) in the I2C specification
|
||||||
|
* @scl_fall_ns: time SCL signal takes to fall in ns; t(f) in the I2C specification
|
||||||
|
* @scl_int_delay_ns: time IP core additionally needs to setup SCL in ns
|
||||||
|
* @sda_fall_ns: time SDA signal takes to fall in ns; t(f) in the I2C specification
|
||||||
|
*/
|
||||||
|
struct i2c_timings {
|
||||||
|
u32 bus_freq_hz;
|
||||||
|
u32 scl_rise_ns;
|
||||||
|
u32 scl_fall_ns;
|
||||||
|
u32 scl_int_delay_ns;
|
||||||
|
u32 sda_fall_ns;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct i2c_bus_recovery_info - I2C bus recovery information
|
* struct i2c_bus_recovery_info - I2C bus recovery information
|
||||||
* @recover_bus: Recover routine. Either pass driver's recover_bus() routine, or
|
* @recover_bus: Recover routine. Either pass driver's recover_bus() routine, or
|
||||||
|
@ -493,6 +509,8 @@ struct i2c_adapter_quirks {
|
||||||
/* convenience macro for typical write-then read case */
|
/* convenience macro for typical write-then read case */
|
||||||
#define I2C_AQ_COMB_WRITE_THEN_READ (I2C_AQ_COMB | I2C_AQ_COMB_WRITE_FIRST | \
|
#define I2C_AQ_COMB_WRITE_THEN_READ (I2C_AQ_COMB | I2C_AQ_COMB_WRITE_FIRST | \
|
||||||
I2C_AQ_COMB_READ_SECOND | I2C_AQ_COMB_SAME_ADDR)
|
I2C_AQ_COMB_READ_SECOND | I2C_AQ_COMB_SAME_ADDR)
|
||||||
|
/* clock stretching is not supported */
|
||||||
|
#define I2C_AQ_NO_CLK_STRETCH BIT(4)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* i2c_adapter is the structure used to identify a physical i2c bus along
|
* i2c_adapter is the structure used to identify a physical i2c bus along
|
||||||
|
@ -602,6 +620,7 @@ extern void i2c_clients_command(struct i2c_adapter *adap,
|
||||||
extern struct i2c_adapter *i2c_get_adapter(int nr);
|
extern struct i2c_adapter *i2c_get_adapter(int nr);
|
||||||
extern void i2c_put_adapter(struct i2c_adapter *adap);
|
extern void i2c_put_adapter(struct i2c_adapter *adap);
|
||||||
|
|
||||||
|
void i2c_parse_fw_timings(struct device *dev, struct i2c_timings *t, bool use_defaults);
|
||||||
|
|
||||||
/* Return the functionality mask */
|
/* Return the functionality mask */
|
||||||
static inline u32 i2c_get_functionality(struct i2c_adapter *adap)
|
static inline u32 i2c_get_functionality(struct i2c_adapter *adap)
|
||||||
|
@ -615,6 +634,20 @@ static inline int i2c_check_functionality(struct i2c_adapter *adap, u32 func)
|
||||||
return (func & i2c_get_functionality(adap)) == func;
|
return (func & i2c_get_functionality(adap)) == func;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* i2c_check_quirks() - Function for checking the quirk flags in an i2c adapter
|
||||||
|
* @adap: i2c adapter
|
||||||
|
* @quirks: quirk flags
|
||||||
|
*
|
||||||
|
* Return: true if the adapter has all the specified quirk flags, false if not
|
||||||
|
*/
|
||||||
|
static inline bool i2c_check_quirks(struct i2c_adapter *adap, u64 quirks)
|
||||||
|
{
|
||||||
|
if (!adap->quirks)
|
||||||
|
return false;
|
||||||
|
return (adap->quirks->flags & quirks) == quirks;
|
||||||
|
}
|
||||||
|
|
||||||
/* Return the adapter number for a specific adapter */
|
/* Return the adapter number for a specific adapter */
|
||||||
static inline int i2c_adapter_id(struct i2c_adapter *adap)
|
static inline int i2c_adapter_id(struct i2c_adapter *adap)
|
||||||
{
|
{
|
||||||
|
@ -622,7 +655,7 @@ static inline int i2c_adapter_id(struct i2c_adapter *adap)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* module_i2c_driver() - Helper macro for registering a I2C driver
|
* module_i2c_driver() - Helper macro for registering a modular I2C driver
|
||||||
* @__i2c_driver: i2c_driver struct
|
* @__i2c_driver: i2c_driver struct
|
||||||
*
|
*
|
||||||
* Helper macro for I2C drivers which do not do anything special in module
|
* Helper macro for I2C drivers which do not do anything special in module
|
||||||
|
@ -633,6 +666,17 @@ static inline int i2c_adapter_id(struct i2c_adapter *adap)
|
||||||
module_driver(__i2c_driver, i2c_add_driver, \
|
module_driver(__i2c_driver, i2c_add_driver, \
|
||||||
i2c_del_driver)
|
i2c_del_driver)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* builtin_i2c_driver() - Helper macro for registering a builtin I2C driver
|
||||||
|
* @__i2c_driver: i2c_driver struct
|
||||||
|
*
|
||||||
|
* Helper macro for I2C drivers which do not do anything special in their
|
||||||
|
* init. This eliminates a lot of boilerplate. Each driver may only
|
||||||
|
* use this macro once, and calling it replaces device_initcall().
|
||||||
|
*/
|
||||||
|
#define builtin_i2c_driver(__i2c_driver) \
|
||||||
|
builtin_driver(__i2c_driver, i2c_add_driver)
|
||||||
|
|
||||||
#endif /* I2C */
|
#endif /* I2C */
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_OF)
|
#if IS_ENABLED(CONFIG_OF)
|
||||||
|
@ -644,6 +688,7 @@ extern struct i2c_adapter *of_find_i2c_adapter_by_node(struct device_node *node)
|
||||||
|
|
||||||
/* must call i2c_put_adapter() when done with returned i2c_adapter device */
|
/* must call i2c_put_adapter() when done with returned i2c_adapter device */
|
||||||
struct i2c_adapter *of_get_i2c_adapter_by_node(struct device_node *node);
|
struct i2c_adapter *of_get_i2c_adapter_by_node(struct device_node *node);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
static inline struct i2c_client *of_find_i2c_device_by_node(struct device_node *node)
|
static inline struct i2c_client *of_find_i2c_device_by_node(struct device_node *node)
|
||||||
|
|
Loading…
Add table
Reference in a new issue