Merge branch 'i2c/for-4.8' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang: "Here is the I2C pull request for 4.8: - the core and i801 driver gained support for SMBus Host Notify - core support for more than one address in DT - i2c_add_adapter() has now better error messages. We can remove all error messages from drivers calling it as a next step. - bigger updates to rk3x driver to support rk3399 SoC - the at24 eeprom driver got refactored and can now read special variants with unique serials or fixed MAC addresses. The rest is regular driver updates and bugfixes" * 'i2c/for-4.8' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (66 commits) i2c: i801: use IS_ENABLED() instead of checking for built-in or module Documentation: i2c: slave: give proper example for pm usage Documentation: i2c: slave: describe buffer problems a bit better i2c: bcm2835: Don't complain on -EPROBE_DEFER from getting our clock i2c: i2c-smbus: drop useless stubs i2c: efm32: fix a failure path in efm32_i2c_probe() Revert "i2c: core: Cleanup I2C ACPI namespace" Revert "i2c: core: Add function for finding the bus speed from ACPI" i2c: Update the description of I2C_SMBUS i2c: i2c-smbus: fix i2c_handle_smbus_host_notify documentation eeprom: at24: tweak the loop_until_timeout() macro eeprom: at24: add support for at24mac series eeprom: at24: support reading the serial number for 24csxx eeprom: at24: platform_data: use BIT() macro eeprom: at24: split at24_eeprom_write() into specialized functions eeprom: at24: split at24_eeprom_read() into specialized functions eeprom: at24: hide the read/write loop behind a macro eeprom: at24: call read/write functions via function pointers eeprom: at24: coding style fixes eeprom: at24: move at24_read() below at24_eeprom_write() ...
This commit is contained in:
Коммит
66304207cd
|
@ -6,10 +6,20 @@ RK3xxx SoCs.
|
||||||
Required properties :
|
Required properties :
|
||||||
|
|
||||||
- reg : Offset and length of the register set for the device
|
- reg : Offset and length of the register set for the device
|
||||||
- compatible : should be "rockchip,rk3066-i2c", "rockchip,rk3188-i2c",
|
- compatible: should be one of the following:
|
||||||
"rockchip,rk3228-i2c" or "rockchip,rk3288-i2c".
|
- "rockchip,rk3066-i2c": for rk3066
|
||||||
|
- "rockchip,rk3188-i2c": for rk3188
|
||||||
|
- "rockchip,rk3228-i2c": for rk3228
|
||||||
|
- "rockchip,rk3288-i2c": for rk3288
|
||||||
|
- "rockchip,rk3399-i2c": for rk3399
|
||||||
- interrupts : interrupt number
|
- interrupts : interrupt number
|
||||||
- clocks : parent clock
|
- clocks: See ../clock/clock-bindings.txt
|
||||||
|
- For older hardware (rk3066, rk3188, rk3228, rk3288):
|
||||||
|
- There is one clock that's used both to derive the functional clock
|
||||||
|
for the device and as the bus clock.
|
||||||
|
- For newer hardware (rk3399): specified by name
|
||||||
|
- "i2c": This is used to derive the functional clock.
|
||||||
|
- "pclk": This is the bus clock.
|
||||||
|
|
||||||
Required on RK3066, RK3188 :
|
Required on RK3066, RK3188 :
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,13 @@ wants to support one of the below features, it should adapt the bindings below.
|
||||||
- wakeup-source
|
- wakeup-source
|
||||||
device can be used as a wakeup source.
|
device can be used as a wakeup source.
|
||||||
|
|
||||||
|
- reg
|
||||||
|
I2C slave addresses
|
||||||
|
|
||||||
|
- reg-names
|
||||||
|
Names of map programmable addresses.
|
||||||
|
It can contain any map needing another address than default one.
|
||||||
|
|
||||||
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
|
||||||
interrupt if not using interrupt names) as primary interrupt for the slave.
|
interrupt if not using interrupt names) as primary interrupt for the slave.
|
||||||
|
|
|
@ -139,9 +139,9 @@ If you want to add slave support to the bus driver:
|
||||||
* implement calls to register/unregister the slave and add those to the
|
* implement calls to register/unregister the slave and add those to the
|
||||||
struct i2c_algorithm. When registering, you probably need to set the i2c
|
struct i2c_algorithm. When registering, you probably need to set the i2c
|
||||||
slave address and enable slave specific interrupts. If you use runtime pm, you
|
slave address and enable slave specific interrupts. If you use runtime pm, you
|
||||||
should use pm_runtime_forbid() because your device usually needs to be powered
|
should use pm_runtime_get_sync() because your device usually needs to be
|
||||||
on always to be able to detect its slave address. When unregistering, do the
|
powered on always to be able to detect its slave address. When unregistering,
|
||||||
inverse of the above.
|
do the inverse of the above.
|
||||||
|
|
||||||
* Catch the slave interrupts and send appropriate i2c_slave_events to the backend.
|
* Catch the slave interrupts and send appropriate i2c_slave_events to the backend.
|
||||||
|
|
||||||
|
@ -173,13 +173,14 @@ During development of this API, the question of using buffers instead of just
|
||||||
bytes came up. Such an extension might be possible, usefulness is unclear at
|
bytes came up. Such an extension might be possible, usefulness is unclear at
|
||||||
this time of writing. Some points to keep in mind when using buffers:
|
this time of writing. Some points to keep in mind when using buffers:
|
||||||
|
|
||||||
* Buffers should be opt-in and slave drivers will always have to support
|
* Buffers should be opt-in and backend drivers will always have to support
|
||||||
byte-based transactions as the ultimate fallback because this is how the
|
byte-based transactions as the ultimate fallback anyhow because this is how
|
||||||
majority of HW works.
|
the majority of HW works.
|
||||||
|
|
||||||
* For backends simulating hardware registers, buffers are not helpful because
|
* For backends simulating hardware registers, buffers are largely not helpful
|
||||||
on writes an action should be immediately triggered. For reads, the data in
|
because after each byte written an action should be immediately triggered.
|
||||||
the buffer might get stale.
|
For reads, the data kept in the buffer might get stale if the backend just
|
||||||
|
updated a register because of internal processing.
|
||||||
|
|
||||||
* A master can send STOP at any time. For partially transferred buffers, this
|
* A master can send STOP at any time. For partially transferred buffers, this
|
||||||
means additional code to handle this exception. Such code tends to be
|
means additional code to handle this exception. Such code tends to be
|
||||||
|
|
|
@ -199,6 +199,12 @@ alerting device's address.
|
||||||
|
|
||||||
[S] [HostAddr] [Wr] A [DevAddr] A [DataLow] A [DataHigh] A [P]
|
[S] [HostAddr] [Wr] A [DevAddr] A [DataLow] A [DataHigh] A [P]
|
||||||
|
|
||||||
|
This is implemented in the following way in the Linux kernel:
|
||||||
|
* I2C bus drivers which support SMBus Host Notify should call
|
||||||
|
i2c_setup_smbus_host_notify() to setup SMBus Host Notify support.
|
||||||
|
* I2C drivers for devices which can trigger SMBus Host Notify should implement
|
||||||
|
the optional alert() callback.
|
||||||
|
|
||||||
|
|
||||||
Packet Error Checking (PEC)
|
Packet Error Checking (PEC)
|
||||||
===========================
|
===========================
|
||||||
|
|
|
@ -568,12 +568,16 @@ static void retry_timeout(unsigned long data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void ssif_alert(struct i2c_client *client, unsigned int data)
|
static void ssif_alert(struct i2c_client *client, enum i2c_alert_protocol type,
|
||||||
|
unsigned int data)
|
||||||
{
|
{
|
||||||
struct ssif_info *ssif_info = i2c_get_clientdata(client);
|
struct ssif_info *ssif_info = i2c_get_clientdata(client);
|
||||||
unsigned long oflags, *flags;
|
unsigned long oflags, *flags;
|
||||||
bool do_get = false;
|
bool do_get = false;
|
||||||
|
|
||||||
|
if (type != I2C_PROTOCOL_SMBUS_ALERT)
|
||||||
|
return;
|
||||||
|
|
||||||
ssif_inc_stat(ssif_info, alerts);
|
ssif_inc_stat(ssif_info, alerts);
|
||||||
|
|
||||||
flags = ipmi_ssif_lock_cond(ssif_info, &oflags);
|
flags = ipmi_ssif_lock_cond(ssif_info, &oflags);
|
||||||
|
|
|
@ -1719,10 +1719,14 @@ static int lm90_probe(struct i2c_client *client,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void lm90_alert(struct i2c_client *client, unsigned int flag)
|
static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type,
|
||||||
|
unsigned int flag)
|
||||||
{
|
{
|
||||||
u16 alarms;
|
u16 alarms;
|
||||||
|
|
||||||
|
if (type != I2C_PROTOCOL_SMBUS_ALERT)
|
||||||
|
return;
|
||||||
|
|
||||||
if (lm90_is_tripped(client, &alarms)) {
|
if (lm90_is_tripped(client, &alarms)) {
|
||||||
/*
|
/*
|
||||||
* Disable ALERT# output, because these chips don't implement
|
* Disable ALERT# output, because these chips don't implement
|
||||||
|
|
|
@ -88,8 +88,8 @@ config I2C_SMBUS
|
||||||
tristate "SMBus-specific protocols" if !I2C_HELPER_AUTO
|
tristate "SMBus-specific protocols" if !I2C_HELPER_AUTO
|
||||||
help
|
help
|
||||||
Say Y here if you want support for SMBus extensions to the I2C
|
Say Y here if you want support for SMBus extensions to the I2C
|
||||||
specification. At the moment, the only supported extension is
|
specification. At the moment, two extensions are supported:
|
||||||
the SMBus alert protocol.
|
the SMBus Alert protocol and the SMBus Host Notify protocol.
|
||||||
|
|
||||||
This support is also available as a module. If so, the module
|
This support is also available as a module. If so, the module
|
||||||
will be called i2c-smbus.
|
will be called i2c-smbus.
|
||||||
|
|
|
@ -91,6 +91,7 @@ config I2C_I801
|
||||||
tristate "Intel 82801 (ICH/PCH)"
|
tristate "Intel 82801 (ICH/PCH)"
|
||||||
depends on PCI
|
depends on PCI
|
||||||
select CHECK_SIGNATURE if X86 && DMI
|
select CHECK_SIGNATURE if X86 && DMI
|
||||||
|
select I2C_SMBUS
|
||||||
help
|
help
|
||||||
If you say yes to this option, support will be included for the Intel
|
If you say yes to this option, support will be included for the Intel
|
||||||
801 family of mainboard I2C interfaces. Specifically, the following
|
801 family of mainboard I2C interfaces. Specifically, the following
|
||||||
|
@ -397,7 +398,7 @@ config I2C_BCM_KONA
|
||||||
|
|
||||||
config I2C_BRCMSTB
|
config I2C_BRCMSTB
|
||||||
tristate "BRCM Settop I2C controller"
|
tristate "BRCM Settop I2C controller"
|
||||||
depends on ARCH_BRCMSTB || COMPILE_TEST
|
depends on ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST
|
||||||
default y
|
default y
|
||||||
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
|
||||||
|
@ -490,7 +491,9 @@ config I2C_DESIGNWARE_PCI
|
||||||
|
|
||||||
config I2C_DESIGNWARE_BAYTRAIL
|
config I2C_DESIGNWARE_BAYTRAIL
|
||||||
bool "Intel Baytrail I2C semaphore support"
|
bool "Intel Baytrail I2C semaphore support"
|
||||||
depends on I2C_DESIGNWARE_PLATFORM && IOSF_MBI=y && ACPI
|
depends on ACPI
|
||||||
|
depends on (I2C_DESIGNWARE_PLATFORM=m && IOSF_MBI) || \
|
||||||
|
(I2C_DESIGNWARE_PLATFORM=y && IOSF_MBI=y)
|
||||||
help
|
help
|
||||||
This driver enables managed host access to the PMIC I2C bus on select
|
This driver enables managed host access to the PMIC I2C bus on select
|
||||||
Intel BayTrail platforms using the X-Powers AXP288 PMIC. It allows
|
Intel BayTrail platforms using the X-Powers AXP288 PMIC. It allows
|
||||||
|
@ -635,7 +638,7 @@ config I2C_LPC2K
|
||||||
|
|
||||||
config I2C_MESON
|
config I2C_MESON
|
||||||
tristate "Amlogic Meson I2C controller"
|
tristate "Amlogic Meson I2C controller"
|
||||||
depends on ARCH_MESON
|
depends on ARCH_MESON || COMPILE_TEST
|
||||||
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 Amlogic Meson family of SoCs.
|
I2C interface on the Amlogic Meson family of SoCs.
|
||||||
|
@ -924,7 +927,7 @@ config I2C_UNIPHIER_F
|
||||||
|
|
||||||
config I2C_VERSATILE
|
config I2C_VERSATILE
|
||||||
tristate "ARM Versatile/Realview I2C bus support"
|
tristate "ARM Versatile/Realview I2C bus support"
|
||||||
depends on ARCH_VERSATILE || ARCH_REALVIEW || ARCH_VEXPRESS
|
depends on ARCH_VERSATILE || ARCH_REALVIEW || ARCH_VEXPRESS || COMPILE_TEST
|
||||||
select I2C_ALGOBIT
|
select I2C_ALGOBIT
|
||||||
help
|
help
|
||||||
Say yes if you want to support the I2C serial bus on ARMs Versatile
|
Say yes if you want to support the I2C serial bus on ARMs Versatile
|
||||||
|
|
|
@ -253,7 +253,8 @@ static int bcm2835_i2c_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
i2c_dev->clk = devm_clk_get(&pdev->dev, NULL);
|
i2c_dev->clk = devm_clk_get(&pdev->dev, NULL);
|
||||||
if (IS_ERR(i2c_dev->clk)) {
|
if (IS_ERR(i2c_dev->clk)) {
|
||||||
dev_err(&pdev->dev, "Could not get clock\n");
|
if (PTR_ERR(i2c_dev->clk) != -EPROBE_DEFER)
|
||||||
|
dev_err(&pdev->dev, "Could not get clock\n");
|
||||||
return PTR_ERR(i2c_dev->clk);
|
return PTR_ERR(i2c_dev->clk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -343,10 +343,9 @@ static int brcmstb_i2c_xfer_bsc_data(struct brcmstb_i2c_dev *dev,
|
||||||
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 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 <= xfersz) {
|
if (no_ack) {
|
||||||
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;
|
||||||
|
|
|
@ -663,7 +663,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
||||||
i2c_dw_xfer_init(dev);
|
i2c_dw_xfer_init(dev);
|
||||||
|
|
||||||
/* wait for tx to complete */
|
/* wait for tx to complete */
|
||||||
if (!wait_for_completion_timeout(&dev->cmd_complete, HZ)) {
|
if (!wait_for_completion_timeout(&dev->cmd_complete, adap->timeout)) {
|
||||||
dev_err(dev->dev, "controller timed out\n");
|
dev_err(dev->dev, "controller timed out\n");
|
||||||
/* i2c_dw_init implicitly disables the adapter */
|
/* i2c_dw_init implicitly disables the adapter */
|
||||||
i2c_dw_init(dev);
|
i2c_dw_init(dev);
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#define DW_IC_CON_MASTER 0x1
|
#define DW_IC_CON_MASTER 0x1
|
||||||
#define DW_IC_CON_SPEED_STD 0x2
|
#define DW_IC_CON_SPEED_STD 0x2
|
||||||
#define DW_IC_CON_SPEED_FAST 0x4
|
#define DW_IC_CON_SPEED_FAST 0x4
|
||||||
|
#define DW_IC_CON_SPEED_MASK 0x6
|
||||||
#define DW_IC_CON_10BITADDR_MASTER 0x10
|
#define DW_IC_CON_10BITADDR_MASTER 0x10
|
||||||
#define DW_IC_CON_RESTART_EN 0x20
|
#define DW_IC_CON_RESTART_EN 0x20
|
||||||
#define DW_IC_CON_SLAVE_DISABLE 0x40
|
#define DW_IC_CON_SLAVE_DISABLE 0x40
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* Copyright (C) 2006 Texas Instruments.
|
* Copyright (C) 2006 Texas Instruments.
|
||||||
* Copyright (C) 2007 MontaVista Software Inc.
|
* Copyright (C) 2007 MontaVista Software Inc.
|
||||||
* Copyright (C) 2009 Provigent Ltd.
|
* Copyright (C) 2009 Provigent Ltd.
|
||||||
* Copyright (C) 2011, 2015 Intel Corporation.
|
* Copyright (C) 2011, 2015, 2016 Intel Corporation.
|
||||||
*
|
*
|
||||||
* ----------------------------------------------------------------------------
|
* ----------------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
|
@ -23,31 +23,27 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/acpi.h>
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/i2c.h>
|
|
||||||
#include <linux/errno.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
#include <linux/errno.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/acpi.h>
|
#include <linux/sched.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
#include "i2c-designware-core.h"
|
#include "i2c-designware-core.h"
|
||||||
|
|
||||||
#define DRIVER_NAME "i2c-designware-pci"
|
#define DRIVER_NAME "i2c-designware-pci"
|
||||||
|
|
||||||
enum dw_pci_ctl_id_t {
|
enum dw_pci_ctl_id_t {
|
||||||
medfield_0,
|
medfield,
|
||||||
medfield_1,
|
merrifield,
|
||||||
medfield_2,
|
|
||||||
medfield_3,
|
|
||||||
medfield_4,
|
|
||||||
medfield_5,
|
|
||||||
|
|
||||||
baytrail,
|
baytrail,
|
||||||
haswell,
|
haswell,
|
||||||
};
|
};
|
||||||
|
@ -68,6 +64,7 @@ struct dw_pci_controller {
|
||||||
u32 clk_khz;
|
u32 clk_khz;
|
||||||
u32 functionality;
|
u32 functionality;
|
||||||
struct dw_scl_sda_cfg *scl_sda_cfg;
|
struct dw_scl_sda_cfg *scl_sda_cfg;
|
||||||
|
int (*setup)(struct pci_dev *pdev, struct dw_pci_controller *c);
|
||||||
};
|
};
|
||||||
|
|
||||||
#define INTEL_MID_STD_CFG (DW_IC_CON_MASTER | \
|
#define INTEL_MID_STD_CFG (DW_IC_CON_MASTER | \
|
||||||
|
@ -80,6 +77,14 @@ struct dw_pci_controller {
|
||||||
I2C_FUNC_SMBUS_WORD_DATA | \
|
I2C_FUNC_SMBUS_WORD_DATA | \
|
||||||
I2C_FUNC_SMBUS_I2C_BLOCK)
|
I2C_FUNC_SMBUS_I2C_BLOCK)
|
||||||
|
|
||||||
|
/* Merrifield HCNT/LCNT/SDA hold time */
|
||||||
|
static struct dw_scl_sda_cfg mrfld_config = {
|
||||||
|
.ss_hcnt = 0x2f8,
|
||||||
|
.fs_hcnt = 0x87,
|
||||||
|
.ss_lcnt = 0x37b,
|
||||||
|
.fs_lcnt = 0x10a,
|
||||||
|
};
|
||||||
|
|
||||||
/* BayTrail HCNT/LCNT/SDA hold time */
|
/* BayTrail HCNT/LCNT/SDA hold time */
|
||||||
static struct dw_scl_sda_cfg byt_config = {
|
static struct dw_scl_sda_cfg byt_config = {
|
||||||
.ss_hcnt = 0x200,
|
.ss_hcnt = 0x200,
|
||||||
|
@ -98,48 +103,60 @@ static struct dw_scl_sda_cfg hsw_config = {
|
||||||
.sda_hold = 0x9,
|
.sda_hold = 0x9,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int mfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
|
||||||
|
{
|
||||||
|
switch (pdev->device) {
|
||||||
|
case 0x0817:
|
||||||
|
c->bus_cfg &= ~DW_IC_CON_SPEED_MASK;
|
||||||
|
c->bus_cfg |= DW_IC_CON_SPEED_STD;
|
||||||
|
case 0x0818:
|
||||||
|
case 0x0819:
|
||||||
|
c->bus_num = pdev->device - 0x817 + 3;
|
||||||
|
return 0;
|
||||||
|
case 0x082C:
|
||||||
|
case 0x082D:
|
||||||
|
case 0x082E:
|
||||||
|
c->bus_num = pdev->device - 0x82C + 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mrfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* On Intel Merrifield the user visible i2c busses are enumerated
|
||||||
|
* [1..7]. So, we add 1 to shift the default range. Besides that the
|
||||||
|
* first PCI slot provides 4 functions, that's why we have to add 0 to
|
||||||
|
* the first slot and 4 to the next one.
|
||||||
|
*/
|
||||||
|
switch (PCI_SLOT(pdev->devfn)) {
|
||||||
|
case 8:
|
||||||
|
c->bus_num = PCI_FUNC(pdev->devfn) + 0 + 1;
|
||||||
|
return 0;
|
||||||
|
case 9:
|
||||||
|
c->bus_num = PCI_FUNC(pdev->devfn) + 4 + 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
static struct dw_pci_controller dw_pci_controllers[] = {
|
static struct dw_pci_controller dw_pci_controllers[] = {
|
||||||
[medfield_0] = {
|
[medfield] = {
|
||||||
.bus_num = 0,
|
.bus_num = -1,
|
||||||
.bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
|
.bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
|
||||||
.tx_fifo_depth = 32,
|
.tx_fifo_depth = 32,
|
||||||
.rx_fifo_depth = 32,
|
.rx_fifo_depth = 32,
|
||||||
.clk_khz = 25000,
|
.clk_khz = 25000,
|
||||||
|
.setup = mfld_setup,
|
||||||
},
|
},
|
||||||
[medfield_1] = {
|
[merrifield] = {
|
||||||
.bus_num = 1,
|
.bus_num = -1,
|
||||||
.bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
|
.bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
|
||||||
.tx_fifo_depth = 32,
|
.tx_fifo_depth = 64,
|
||||||
.rx_fifo_depth = 32,
|
.rx_fifo_depth = 64,
|
||||||
.clk_khz = 25000,
|
.scl_sda_cfg = &mrfld_config,
|
||||||
},
|
.setup = mrfld_setup,
|
||||||
[medfield_2] = {
|
|
||||||
.bus_num = 2,
|
|
||||||
.bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
|
|
||||||
.tx_fifo_depth = 32,
|
|
||||||
.rx_fifo_depth = 32,
|
|
||||||
.clk_khz = 25000,
|
|
||||||
},
|
|
||||||
[medfield_3] = {
|
|
||||||
.bus_num = 3,
|
|
||||||
.bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_STD,
|
|
||||||
.tx_fifo_depth = 32,
|
|
||||||
.rx_fifo_depth = 32,
|
|
||||||
.clk_khz = 25000,
|
|
||||||
},
|
|
||||||
[medfield_4] = {
|
|
||||||
.bus_num = 4,
|
|
||||||
.bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
|
|
||||||
.tx_fifo_depth = 32,
|
|
||||||
.rx_fifo_depth = 32,
|
|
||||||
.clk_khz = 25000,
|
|
||||||
},
|
|
||||||
[medfield_5] = {
|
|
||||||
.bus_num = 5,
|
|
||||||
.bus_cfg = INTEL_MID_STD_CFG | DW_IC_CON_SPEED_FAST,
|
|
||||||
.tx_fifo_depth = 32,
|
|
||||||
.rx_fifo_depth = 32,
|
|
||||||
.clk_khz = 25000,
|
|
||||||
},
|
},
|
||||||
[baytrail] = {
|
[baytrail] = {
|
||||||
.bus_num = -1,
|
.bus_num = -1,
|
||||||
|
@ -190,7 +207,7 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
|
||||||
struct dw_i2c_dev *dev;
|
struct dw_i2c_dev *dev;
|
||||||
struct i2c_adapter *adap;
|
struct i2c_adapter *adap;
|
||||||
int r;
|
int r;
|
||||||
struct dw_pci_controller *controller;
|
struct dw_pci_controller *controller;
|
||||||
struct dw_scl_sda_cfg *cfg;
|
struct dw_scl_sda_cfg *cfg;
|
||||||
|
|
||||||
if (id->driver_data >= ARRAY_SIZE(dw_pci_controllers)) {
|
if (id->driver_data >= ARRAY_SIZE(dw_pci_controllers)) {
|
||||||
|
@ -224,6 +241,13 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
|
||||||
dev->base = pcim_iomap_table(pdev)[0];
|
dev->base = pcim_iomap_table(pdev)[0];
|
||||||
dev->dev = &pdev->dev;
|
dev->dev = &pdev->dev;
|
||||||
dev->irq = pdev->irq;
|
dev->irq = pdev->irq;
|
||||||
|
|
||||||
|
if (controller->setup) {
|
||||||
|
r = controller->setup(pdev, controller);
|
||||||
|
if (r)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
dev->functionality = controller->functionality |
|
dev->functionality = controller->functionality |
|
||||||
DW_DEFAULT_FUNCTIONALITY;
|
DW_DEFAULT_FUNCTIONALITY;
|
||||||
|
|
||||||
|
@ -276,12 +300,15 @@ MODULE_ALIAS("i2c_designware-pci");
|
||||||
|
|
||||||
static const struct pci_device_id i2_designware_pci_ids[] = {
|
static const struct pci_device_id i2_designware_pci_ids[] = {
|
||||||
/* Medfield */
|
/* Medfield */
|
||||||
{ PCI_VDEVICE(INTEL, 0x0817), medfield_3 },
|
{ PCI_VDEVICE(INTEL, 0x0817), medfield },
|
||||||
{ PCI_VDEVICE(INTEL, 0x0818), medfield_4 },
|
{ PCI_VDEVICE(INTEL, 0x0818), medfield },
|
||||||
{ PCI_VDEVICE(INTEL, 0x0819), medfield_5 },
|
{ PCI_VDEVICE(INTEL, 0x0819), medfield },
|
||||||
{ PCI_VDEVICE(INTEL, 0x082C), medfield_0 },
|
{ PCI_VDEVICE(INTEL, 0x082C), medfield },
|
||||||
{ PCI_VDEVICE(INTEL, 0x082D), medfield_1 },
|
{ PCI_VDEVICE(INTEL, 0x082D), medfield },
|
||||||
{ PCI_VDEVICE(INTEL, 0x082E), medfield_2 },
|
{ PCI_VDEVICE(INTEL, 0x082E), medfield },
|
||||||
|
/* Merrifield */
|
||||||
|
{ PCI_VDEVICE(INTEL, 0x1195), merrifield },
|
||||||
|
{ PCI_VDEVICE(INTEL, 0x1196), merrifield },
|
||||||
/* Baytrail */
|
/* Baytrail */
|
||||||
{ PCI_VDEVICE(INTEL, 0x0F41), baytrail },
|
{ PCI_VDEVICE(INTEL, 0x0F41), baytrail },
|
||||||
{ PCI_VDEVICE(INTEL, 0x0F42), baytrail },
|
{ PCI_VDEVICE(INTEL, 0x0F42), baytrail },
|
||||||
|
|
|
@ -433,7 +433,7 @@ static int efm32_i2c_probe(struct platform_device *pdev)
|
||||||
ret = request_irq(ddata->irq, efm32_i2c_irq, 0, DRIVER_NAME, ddata);
|
ret = request_irq(ddata->irq, efm32_i2c_irq, 0, DRIVER_NAME, ddata);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "failed to request irq (%d)\n", ret);
|
dev_err(&pdev->dev, "failed to request irq (%d)\n", ret);
|
||||||
return ret;
|
goto err_disable_clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = i2c_add_adapter(&ddata->adapter);
|
ret = i2c_add_adapter(&ddata->adapter);
|
||||||
|
|
|
@ -319,16 +319,6 @@ static struct isa_driver i2c_elektor_driver = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init i2c_pcfisa_init(void)
|
|
||||||
{
|
|
||||||
return isa_register_driver(&i2c_elektor_driver, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __exit i2c_pcfisa_exit(void)
|
|
||||||
{
|
|
||||||
isa_unregister_driver(&i2c_elektor_driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
MODULE_AUTHOR("Hans Berglund <hb@spacetec.no>");
|
MODULE_AUTHOR("Hans Berglund <hb@spacetec.no>");
|
||||||
MODULE_DESCRIPTION("I2C-Bus adapter routines for PCF8584 ISA bus adapter");
|
MODULE_DESCRIPTION("I2C-Bus adapter routines for PCF8584 ISA bus adapter");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
@ -338,6 +328,4 @@ module_param(irq, int, 0);
|
||||||
module_param(clock, int, 0);
|
module_param(clock, int, 0);
|
||||||
module_param(own, int, 0);
|
module_param(own, int, 0);
|
||||||
module_param(mmapped, int, 0);
|
module_param(mmapped, int, 0);
|
||||||
|
module_isa_driver(i2c_elektor_driver, 1);
|
||||||
module_init(i2c_pcfisa_init);
|
|
||||||
module_exit(i2c_pcfisa_exit);
|
|
||||||
|
|
|
@ -72,6 +72,7 @@
|
||||||
* Block process call transaction no
|
* Block process call transaction no
|
||||||
* I2C block read transaction yes (doesn't use the block buffer)
|
* I2C block read transaction yes (doesn't use the block buffer)
|
||||||
* Slave mode no
|
* Slave mode no
|
||||||
|
* SMBus Host Notify yes
|
||||||
* Interrupt processing yes
|
* Interrupt processing yes
|
||||||
*
|
*
|
||||||
* See the file Documentation/i2c/busses/i2c-i801 for details.
|
* See the file Documentation/i2c/busses/i2c-i801 for details.
|
||||||
|
@ -86,6 +87,7 @@
|
||||||
#include <linux/ioport.h>
|
#include <linux/ioport.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/i2c-smbus.h>
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/dmi.h>
|
#include <linux/dmi.h>
|
||||||
|
@ -96,8 +98,7 @@
|
||||||
#include <linux/platform_data/itco_wdt.h>
|
#include <linux/platform_data/itco_wdt.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
|
|
||||||
#if (defined CONFIG_I2C_MUX_GPIO || defined CONFIG_I2C_MUX_GPIO_MODULE) && \
|
#if IS_ENABLED(CONFIG_I2C_MUX_GPIO) && defined CONFIG_DMI
|
||||||
defined CONFIG_DMI
|
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/i2c-mux-gpio.h>
|
#include <linux/i2c-mux-gpio.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -113,6 +114,10 @@
|
||||||
#define SMBPEC(p) (8 + (p)->smba) /* ICH3 and later */
|
#define SMBPEC(p) (8 + (p)->smba) /* ICH3 and later */
|
||||||
#define SMBAUXSTS(p) (12 + (p)->smba) /* ICH4 and later */
|
#define SMBAUXSTS(p) (12 + (p)->smba) /* ICH4 and later */
|
||||||
#define SMBAUXCTL(p) (13 + (p)->smba) /* ICH4 and later */
|
#define SMBAUXCTL(p) (13 + (p)->smba) /* ICH4 and later */
|
||||||
|
#define SMBSLVSTS(p) (16 + (p)->smba) /* ICH3 and later */
|
||||||
|
#define SMBSLVCMD(p) (17 + (p)->smba) /* ICH3 and later */
|
||||||
|
#define SMBNTFDADD(p) (20 + (p)->smba) /* ICH3 and later */
|
||||||
|
#define SMBNTFDDAT(p) (22 + (p)->smba) /* ICH3 and later */
|
||||||
|
|
||||||
/* PCI Address Constants */
|
/* PCI Address Constants */
|
||||||
#define SMBBAR 4
|
#define SMBBAR 4
|
||||||
|
@ -144,6 +149,10 @@
|
||||||
/* TCO configuration bits for TCOCTL */
|
/* TCO configuration bits for TCOCTL */
|
||||||
#define TCOCTL_EN 0x0100
|
#define TCOCTL_EN 0x0100
|
||||||
|
|
||||||
|
/* Auxiliary status register bits, ICH4+ only */
|
||||||
|
#define SMBAUXSTS_CRCE 1
|
||||||
|
#define SMBAUXSTS_STCO 2
|
||||||
|
|
||||||
/* Auxiliary control register bits, ICH4+ only */
|
/* Auxiliary control register bits, ICH4+ only */
|
||||||
#define SMBAUXCTL_CRC 1
|
#define SMBAUXCTL_CRC 1
|
||||||
#define SMBAUXCTL_E32B 2
|
#define SMBAUXCTL_E32B 2
|
||||||
|
@ -177,6 +186,12 @@
|
||||||
#define SMBHSTSTS_INTR 0x02
|
#define SMBHSTSTS_INTR 0x02
|
||||||
#define SMBHSTSTS_HOST_BUSY 0x01
|
#define SMBHSTSTS_HOST_BUSY 0x01
|
||||||
|
|
||||||
|
/* Host Notify Status registers bits */
|
||||||
|
#define SMBSLVSTS_HST_NTFY_STS 1
|
||||||
|
|
||||||
|
/* Host Notify Command registers bits */
|
||||||
|
#define SMBSLVCMD_HST_NTFY_INTREN 0x01
|
||||||
|
|
||||||
#define STATUS_ERROR_FLAGS (SMBHSTSTS_FAILED | SMBHSTSTS_BUS_ERR | \
|
#define STATUS_ERROR_FLAGS (SMBHSTSTS_FAILED | SMBHSTSTS_BUS_ERR | \
|
||||||
SMBHSTSTS_DEV_ERR)
|
SMBHSTSTS_DEV_ERR)
|
||||||
|
|
||||||
|
@ -239,8 +254,7 @@ struct i801_priv {
|
||||||
int len;
|
int len;
|
||||||
u8 *data;
|
u8 *data;
|
||||||
|
|
||||||
#if (defined CONFIG_I2C_MUX_GPIO || defined CONFIG_I2C_MUX_GPIO_MODULE) && \
|
#if IS_ENABLED(CONFIG_I2C_MUX_GPIO) && defined CONFIG_DMI
|
||||||
defined CONFIG_DMI
|
|
||||||
const struct i801_mux_config *mux_drvdata;
|
const struct i801_mux_config *mux_drvdata;
|
||||||
struct platform_device *mux_pdev;
|
struct platform_device *mux_pdev;
|
||||||
#endif
|
#endif
|
||||||
|
@ -252,13 +266,17 @@ struct i801_priv {
|
||||||
*/
|
*/
|
||||||
bool acpi_reserved;
|
bool acpi_reserved;
|
||||||
struct mutex acpi_lock;
|
struct mutex acpi_lock;
|
||||||
|
struct smbus_host_notify *host_notify;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define SMBHSTNTFY_SIZE 8
|
||||||
|
|
||||||
#define FEATURE_SMBUS_PEC (1 << 0)
|
#define FEATURE_SMBUS_PEC (1 << 0)
|
||||||
#define FEATURE_BLOCK_BUFFER (1 << 1)
|
#define FEATURE_BLOCK_BUFFER (1 << 1)
|
||||||
#define FEATURE_BLOCK_PROC (1 << 2)
|
#define FEATURE_BLOCK_PROC (1 << 2)
|
||||||
#define FEATURE_I2C_BLOCK_READ (1 << 3)
|
#define FEATURE_I2C_BLOCK_READ (1 << 3)
|
||||||
#define FEATURE_IRQ (1 << 4)
|
#define FEATURE_IRQ (1 << 4)
|
||||||
|
#define FEATURE_HOST_NOTIFY (1 << 5)
|
||||||
/* Not really a feature, but it's convenient to handle it as such */
|
/* Not really a feature, but it's convenient to handle it as such */
|
||||||
#define FEATURE_IDF (1 << 15)
|
#define FEATURE_IDF (1 << 15)
|
||||||
#define FEATURE_TCO (1 << 16)
|
#define FEATURE_TCO (1 << 16)
|
||||||
|
@ -269,6 +287,7 @@ static const char *i801_feature_names[] = {
|
||||||
"Block process call",
|
"Block process call",
|
||||||
"I2C block read",
|
"I2C block read",
|
||||||
"Interrupt",
|
"Interrupt",
|
||||||
|
"SMBus Host Notify",
|
||||||
};
|
};
|
||||||
|
|
||||||
static unsigned int disable_features;
|
static unsigned int disable_features;
|
||||||
|
@ -277,7 +296,8 @@ MODULE_PARM_DESC(disable_features, "Disable selected driver features:\n"
|
||||||
"\t\t 0x01 disable SMBus PEC\n"
|
"\t\t 0x01 disable SMBus PEC\n"
|
||||||
"\t\t 0x02 disable the block buffer\n"
|
"\t\t 0x02 disable the block buffer\n"
|
||||||
"\t\t 0x08 disable the I2C block read functionality\n"
|
"\t\t 0x08 disable the I2C block read functionality\n"
|
||||||
"\t\t 0x10 don't use interrupts ");
|
"\t\t 0x10 don't use interrupts\n"
|
||||||
|
"\t\t 0x20 disable SMBus Host Notify ");
|
||||||
|
|
||||||
/* Make sure the SMBus host is ready to start transmitting.
|
/* Make sure the SMBus host is ready to start transmitting.
|
||||||
Return 0 if it is, -EBUSY if it is not. */
|
Return 0 if it is, -EBUSY if it is not. */
|
||||||
|
@ -305,6 +325,29 @@ static int i801_check_pre(struct i801_priv *priv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clear CRC status if needed.
|
||||||
|
* During normal operation, i801_check_post() takes care
|
||||||
|
* of it after every operation. We do it here only in case
|
||||||
|
* the hardware was already in this state when the driver
|
||||||
|
* started.
|
||||||
|
*/
|
||||||
|
if (priv->features & FEATURE_SMBUS_PEC) {
|
||||||
|
status = inb_p(SMBAUXSTS(priv)) & SMBAUXSTS_CRCE;
|
||||||
|
if (status) {
|
||||||
|
dev_dbg(&priv->pci_dev->dev,
|
||||||
|
"Clearing aux status flags (%02x)\n", status);
|
||||||
|
outb_p(status, SMBAUXSTS(priv));
|
||||||
|
status = inb_p(SMBAUXSTS(priv)) & SMBAUXSTS_CRCE;
|
||||||
|
if (status) {
|
||||||
|
dev_err(&priv->pci_dev->dev,
|
||||||
|
"Failed clearing aux status flags (%02x)\n",
|
||||||
|
status);
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,8 +391,30 @@ static int i801_check_post(struct i801_priv *priv, int status)
|
||||||
dev_err(&priv->pci_dev->dev, "Transaction failed\n");
|
dev_err(&priv->pci_dev->dev, "Transaction failed\n");
|
||||||
}
|
}
|
||||||
if (status & SMBHSTSTS_DEV_ERR) {
|
if (status & SMBHSTSTS_DEV_ERR) {
|
||||||
result = -ENXIO;
|
/*
|
||||||
dev_dbg(&priv->pci_dev->dev, "No response\n");
|
* This may be a PEC error, check and clear it.
|
||||||
|
*
|
||||||
|
* AUXSTS is handled differently from HSTSTS.
|
||||||
|
* For HSTSTS, i801_isr() or i801_wait_intr()
|
||||||
|
* has already cleared the error bits in hardware,
|
||||||
|
* and we are passed a copy of the original value
|
||||||
|
* in "status".
|
||||||
|
* For AUXSTS, the hardware register is left
|
||||||
|
* for us to handle here.
|
||||||
|
* This is asymmetric, slightly iffy, but safe,
|
||||||
|
* since all this code is serialized and the CRCE
|
||||||
|
* bit is harmless as long as it's cleared before
|
||||||
|
* the next operation.
|
||||||
|
*/
|
||||||
|
if ((priv->features & FEATURE_SMBUS_PEC) &&
|
||||||
|
(inb_p(SMBAUXSTS(priv)) & SMBAUXSTS_CRCE)) {
|
||||||
|
outb_p(SMBAUXSTS_CRCE, SMBAUXSTS(priv));
|
||||||
|
result = -EBADMSG;
|
||||||
|
dev_dbg(&priv->pci_dev->dev, "PEC error\n");
|
||||||
|
} else {
|
||||||
|
result = -ENXIO;
|
||||||
|
dev_dbg(&priv->pci_dev->dev, "No response\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (status & SMBHSTSTS_BUS_ERR) {
|
if (status & SMBHSTSTS_BUS_ERR) {
|
||||||
result = -EAGAIN;
|
result = -EAGAIN;
|
||||||
|
@ -511,8 +576,23 @@ static void i801_isr_byte_done(struct i801_priv *priv)
|
||||||
outb_p(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv));
|
outb_p(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static irqreturn_t i801_host_notify_isr(struct i801_priv *priv)
|
||||||
|
{
|
||||||
|
unsigned short addr;
|
||||||
|
unsigned int data;
|
||||||
|
|
||||||
|
addr = inb_p(SMBNTFDADD(priv)) >> 1;
|
||||||
|
data = inw_p(SMBNTFDDAT(priv));
|
||||||
|
|
||||||
|
i2c_handle_smbus_host_notify(priv->host_notify, addr, data);
|
||||||
|
|
||||||
|
/* clear Host Notify bit and return */
|
||||||
|
outb_p(SMBSLVSTS_HST_NTFY_STS, SMBSLVSTS(priv));
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* There are two kinds of interrupts:
|
* There are three kinds of interrupts:
|
||||||
*
|
*
|
||||||
* 1) i801 signals transaction completion with one of these interrupts:
|
* 1) i801 signals transaction completion with one of these interrupts:
|
||||||
* INTR - Success
|
* INTR - Success
|
||||||
|
@ -524,6 +604,8 @@ static void i801_isr_byte_done(struct i801_priv *priv)
|
||||||
*
|
*
|
||||||
* 2) For byte-by-byte (I2C read/write) transactions, one BYTE_DONE interrupt
|
* 2) For byte-by-byte (I2C read/write) transactions, one BYTE_DONE interrupt
|
||||||
* occurs for each byte of a byte-by-byte to prepare the next byte.
|
* occurs for each byte of a byte-by-byte to prepare the next byte.
|
||||||
|
*
|
||||||
|
* 3) Host Notify interrupts
|
||||||
*/
|
*/
|
||||||
static irqreturn_t i801_isr(int irq, void *dev_id)
|
static irqreturn_t i801_isr(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
|
@ -536,6 +618,12 @@ static irqreturn_t i801_isr(int irq, void *dev_id)
|
||||||
if (!(pcists & SMBPCISTS_INTS))
|
if (!(pcists & SMBPCISTS_INTS))
|
||||||
return IRQ_NONE;
|
return IRQ_NONE;
|
||||||
|
|
||||||
|
if (priv->features & FEATURE_HOST_NOTIFY) {
|
||||||
|
status = inb_p(SMBSLVSTS(priv));
|
||||||
|
if (status & SMBSLVSTS_HST_NTFY_STS)
|
||||||
|
return i801_host_notify_isr(priv);
|
||||||
|
}
|
||||||
|
|
||||||
status = inb_p(SMBHSTSTS(priv));
|
status = inb_p(SMBHSTSTS(priv));
|
||||||
if (status & SMBHSTSTS_BYTE_DONE)
|
if (status & SMBHSTSTS_BYTE_DONE)
|
||||||
i801_isr_byte_done(priv);
|
i801_isr_byte_done(priv);
|
||||||
|
@ -547,7 +635,7 @@ static irqreturn_t i801_isr(int irq, void *dev_id)
|
||||||
status &= SMBHSTSTS_INTR | STATUS_ERROR_FLAGS;
|
status &= SMBHSTSTS_INTR | STATUS_ERROR_FLAGS;
|
||||||
if (status) {
|
if (status) {
|
||||||
outb_p(status, SMBHSTSTS(priv));
|
outb_p(status, SMBHSTSTS(priv));
|
||||||
priv->status |= status;
|
priv->status = status;
|
||||||
wake_up(&priv->waitq);
|
wake_up(&priv->waitq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -847,7 +935,28 @@ static u32 i801_func(struct i2c_adapter *adapter)
|
||||||
I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK |
|
I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK |
|
||||||
((priv->features & FEATURE_SMBUS_PEC) ? I2C_FUNC_SMBUS_PEC : 0) |
|
((priv->features & FEATURE_SMBUS_PEC) ? I2C_FUNC_SMBUS_PEC : 0) |
|
||||||
((priv->features & FEATURE_I2C_BLOCK_READ) ?
|
((priv->features & FEATURE_I2C_BLOCK_READ) ?
|
||||||
I2C_FUNC_SMBUS_READ_I2C_BLOCK : 0);
|
I2C_FUNC_SMBUS_READ_I2C_BLOCK : 0) |
|
||||||
|
((priv->features & FEATURE_HOST_NOTIFY) ?
|
||||||
|
I2C_FUNC_SMBUS_HOST_NOTIFY : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int i801_enable_host_notify(struct i2c_adapter *adapter)
|
||||||
|
{
|
||||||
|
struct i801_priv *priv = i2c_get_adapdata(adapter);
|
||||||
|
|
||||||
|
if (!(priv->features & FEATURE_HOST_NOTIFY))
|
||||||
|
return -ENOTSUPP;
|
||||||
|
|
||||||
|
if (!priv->host_notify)
|
||||||
|
priv->host_notify = i2c_setup_smbus_host_notify(adapter);
|
||||||
|
if (!priv->host_notify)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
outb_p(SMBSLVCMD_HST_NTFY_INTREN, SMBSLVCMD(priv));
|
||||||
|
/* clear Host Notify bit to allow a new notification */
|
||||||
|
outb_p(SMBSLVSTS_HST_NTFY_STS, SMBSLVSTS(priv));
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct i2c_algorithm smbus_algorithm = {
|
static const struct i2c_algorithm smbus_algorithm = {
|
||||||
|
@ -1022,8 +1131,7 @@ static void __init input_apanel_init(void) {}
|
||||||
static void i801_probe_optional_slaves(struct i801_priv *priv) {}
|
static void i801_probe_optional_slaves(struct i801_priv *priv) {}
|
||||||
#endif /* CONFIG_X86 && CONFIG_DMI */
|
#endif /* CONFIG_X86 && CONFIG_DMI */
|
||||||
|
|
||||||
#if (defined CONFIG_I2C_MUX_GPIO || defined CONFIG_I2C_MUX_GPIO_MODULE) && \
|
#if IS_ENABLED(CONFIG_I2C_MUX_GPIO) && defined CONFIG_DMI
|
||||||
defined CONFIG_DMI
|
|
||||||
static struct i801_mux_config i801_mux_config_asus_z8_d12 = {
|
static struct i801_mux_config i801_mux_config_asus_z8_d12 = {
|
||||||
.gpio_chip = "gpio_ich",
|
.gpio_chip = "gpio_ich",
|
||||||
.values = { 0x02, 0x03 },
|
.values = { 0x02, 0x03 },
|
||||||
|
@ -1379,6 +1487,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||||
priv->features |= FEATURE_SMBUS_PEC;
|
priv->features |= FEATURE_SMBUS_PEC;
|
||||||
priv->features |= FEATURE_BLOCK_BUFFER;
|
priv->features |= FEATURE_BLOCK_BUFFER;
|
||||||
priv->features |= FEATURE_TCO;
|
priv->features |= FEATURE_TCO;
|
||||||
|
priv->features |= FEATURE_HOST_NOTIFY;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF0:
|
case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF0:
|
||||||
|
@ -1398,6 +1507,8 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||||
priv->features |= FEATURE_BLOCK_BUFFER;
|
priv->features |= FEATURE_BLOCK_BUFFER;
|
||||||
/* fall through */
|
/* fall through */
|
||||||
case PCI_DEVICE_ID_INTEL_82801CA_3:
|
case PCI_DEVICE_ID_INTEL_82801CA_3:
|
||||||
|
priv->features |= FEATURE_HOST_NOTIFY;
|
||||||
|
/* fall through */
|
||||||
case PCI_DEVICE_ID_INTEL_82801BA_2:
|
case PCI_DEVICE_ID_INTEL_82801BA_2:
|
||||||
case PCI_DEVICE_ID_INTEL_82801AB_3:
|
case PCI_DEVICE_ID_INTEL_82801AB_3:
|
||||||
case PCI_DEVICE_ID_INTEL_82801AA_3:
|
case PCI_DEVICE_ID_INTEL_82801AA_3:
|
||||||
|
@ -1507,6 +1618,15 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enable Host Notify for chips that supports it.
|
||||||
|
* It is done after i2c_add_adapter() so that we are sure the work queue
|
||||||
|
* is not used if i2c_add_adapter() fails.
|
||||||
|
*/
|
||||||
|
err = i801_enable_host_notify(&priv->adapter);
|
||||||
|
if (err && err != -ENOTSUPP)
|
||||||
|
dev_warn(&dev->dev, "Unable to enable SMBus Host Notify\n");
|
||||||
|
|
||||||
i801_probe_optional_slaves(priv);
|
i801_probe_optional_slaves(priv);
|
||||||
/* We ignore errors - multiplexing is optional */
|
/* We ignore errors - multiplexing is optional */
|
||||||
i801_add_mux(priv);
|
i801_add_mux(priv);
|
||||||
|
@ -1553,6 +1673,14 @@ static int i801_suspend(struct device *dev)
|
||||||
|
|
||||||
static int i801_resume(struct device *dev)
|
static int i801_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
|
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||||
|
struct i801_priv *priv = pci_get_drvdata(pci_dev);
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = i801_enable_host_notify(&priv->adapter);
|
||||||
|
if (err && err != -ENOTSUPP)
|
||||||
|
dev_warn(dev, "Unable to enable SMBus Host Notify\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -791,10 +791,6 @@ static int jz4780_i2c_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
jz4780_i2c_writew(i2c, JZ4780_I2C_INTM, 0x0);
|
jz4780_i2c_writew(i2c, JZ4780_I2C_INTM, 0x0);
|
||||||
|
|
||||||
i2c->cmd = 0;
|
|
||||||
memset(i2c->cmd_buf, 0, BUFSIZE);
|
|
||||||
memset(i2c->data_buf, 0, BUFSIZE);
|
|
||||||
|
|
||||||
i2c->irq = platform_get_irq(pdev, 0);
|
i2c->irq = platform_get_irq(pdev, 0);
|
||||||
ret = devm_request_irq(&pdev->dev, i2c->irq, jz4780_i2c_irq, 0,
|
ret = devm_request_irq(&pdev->dev, i2c->irq, jz4780_i2c_irq, 0,
|
||||||
dev_name(&pdev->dev), i2c);
|
dev_name(&pdev->dev), i2c);
|
||||||
|
|
|
@ -193,23 +193,12 @@ static struct isa_driver pca_isa_driver = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init pca_isa_init(void)
|
|
||||||
{
|
|
||||||
return isa_register_driver(&pca_isa_driver, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __exit pca_isa_exit(void)
|
|
||||||
{
|
|
||||||
isa_unregister_driver(&pca_isa_driver);
|
|
||||||
}
|
|
||||||
|
|
||||||
MODULE_AUTHOR("Ian Campbell <icampbell@arcom.com>");
|
MODULE_AUTHOR("Ian Campbell <icampbell@arcom.com>");
|
||||||
MODULE_DESCRIPTION("ISA base PCA9564/PCA9665 driver");
|
MODULE_DESCRIPTION("ISA base PCA9564/PCA9665 driver");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
module_param(base, ulong, 0);
|
module_param(base, ulong, 0);
|
||||||
MODULE_PARM_DESC(base, "I/O base address");
|
MODULE_PARM_DESC(base, "I/O base address");
|
||||||
|
|
||||||
module_param(irq, int, 0);
|
module_param(irq, int, 0);
|
||||||
MODULE_PARM_DESC(irq, "IRQ");
|
MODULE_PARM_DESC(irq, "IRQ");
|
||||||
module_param(clock, int, 0);
|
module_param(clock, int, 0);
|
||||||
|
@ -220,6 +209,4 @@ MODULE_PARM_DESC(clock, "Clock rate in hertz.\n\t\t"
|
||||||
"\t\t\t\tFast: 100100 - 400099\n"
|
"\t\t\t\tFast: 100100 - 400099\n"
|
||||||
"\t\t\t\tFast+: 400100 - 10000099\n"
|
"\t\t\t\tFast+: 400100 - 10000099\n"
|
||||||
"\t\t\t\tTurbo: Up to 1265800");
|
"\t\t\t\tTurbo: Up to 1265800");
|
||||||
|
module_isa_driver(pca_isa_driver, 1);
|
||||||
module_init(pca_isa_init);
|
|
||||||
module_exit(pca_isa_exit);
|
|
||||||
|
|
|
@ -213,14 +213,16 @@ static irqreturn_t qup_i2c_interrupt(int irq, void *dev)
|
||||||
bus_err &= I2C_STATUS_ERROR_MASK;
|
bus_err &= I2C_STATUS_ERROR_MASK;
|
||||||
qup_err &= QUP_STATUS_ERROR_FLAGS;
|
qup_err &= QUP_STATUS_ERROR_FLAGS;
|
||||||
|
|
||||||
if (qup_err) {
|
/* Clear the error bits in QUP_ERROR_FLAGS */
|
||||||
/* Clear Error interrupt */
|
if (qup_err)
|
||||||
writel(qup_err, qup->base + QUP_ERROR_FLAGS);
|
writel(qup_err, qup->base + QUP_ERROR_FLAGS);
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bus_err) {
|
/* Clear the error bits in QUP_I2C_STATUS */
|
||||||
/* Clear Error interrupt */
|
if (bus_err)
|
||||||
|
writel(bus_err, qup->base + QUP_I2C_STATUS);
|
||||||
|
|
||||||
|
/* Reset the QUP State in case of error */
|
||||||
|
if (qup_err || bus_err) {
|
||||||
writel(QUP_RESET_STATE, qup->base + QUP_STATE);
|
writel(QUP_RESET_STATE, qup->base + QUP_STATE);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
@ -310,6 +312,7 @@ static int qup_i2c_wait_ready(struct qup_i2c_dev *qup, int op, bool val,
|
||||||
u32 opflags;
|
u32 opflags;
|
||||||
u32 status;
|
u32 status;
|
||||||
u32 shift = __ffs(op);
|
u32 shift = __ffs(op);
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
len *= qup->one_byte_t;
|
len *= qup->one_byte_t;
|
||||||
/* timeout after a wait of twice the max time */
|
/* timeout after a wait of twice the max time */
|
||||||
|
@ -321,18 +324,28 @@ static int qup_i2c_wait_ready(struct qup_i2c_dev *qup, int op, bool val,
|
||||||
|
|
||||||
if (((opflags & op) >> shift) == val) {
|
if (((opflags & op) >> shift) == val) {
|
||||||
if ((op == QUP_OUT_NOT_EMPTY) && qup->is_last) {
|
if ((op == QUP_OUT_NOT_EMPTY) && qup->is_last) {
|
||||||
if (!(status & I2C_STATUS_BUS_ACTIVE))
|
if (!(status & I2C_STATUS_BUS_ACTIVE)) {
|
||||||
return 0;
|
ret = 0;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
ret = 0;
|
||||||
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (time_after(jiffies, timeout))
|
if (time_after(jiffies, timeout)) {
|
||||||
return -ETIMEDOUT;
|
ret = -ETIMEDOUT;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
usleep_range(len, len * 2);
|
usleep_range(len, len * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (qup->bus_err || qup->qup_err)
|
||||||
|
ret = (qup->bus_err & QUP_I2C_NACK_FLAG) ? -ENXIO : -EIO;
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qup_i2c_set_write_mode_v2(struct qup_i2c_dev *qup,
|
static void qup_i2c_set_write_mode_v2(struct qup_i2c_dev *qup,
|
||||||
|
@ -585,8 +598,8 @@ static void qup_i2c_bam_cb(void *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qup_sg_set_buf(struct scatterlist *sg, void *buf,
|
static int qup_sg_set_buf(struct scatterlist *sg, void *buf,
|
||||||
struct qup_i2c_tag *tg, unsigned int buflen,
|
unsigned int buflen, struct qup_i2c_dev *qup,
|
||||||
struct qup_i2c_dev *qup, int map, int dir)
|
int dir)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -595,9 +608,6 @@ static int qup_sg_set_buf(struct scatterlist *sg, void *buf,
|
||||||
if (!ret)
|
if (!ret)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (!map)
|
|
||||||
sg_dma_address(sg) = tg->addr + ((u8 *)buf - tg->start);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -649,37 +659,37 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
|
||||||
u8 *tags;
|
u8 *tags;
|
||||||
|
|
||||||
while (idx < num) {
|
while (idx < num) {
|
||||||
blocks = (msg->len + limit) / limit;
|
|
||||||
rem = msg->len % limit;
|
|
||||||
tx_len = 0, len = 0, i = 0;
|
tx_len = 0, len = 0, i = 0;
|
||||||
|
|
||||||
qup->is_last = (idx == (num - 1));
|
qup->is_last = (idx == (num - 1));
|
||||||
|
|
||||||
qup_i2c_set_blk_data(qup, msg);
|
qup_i2c_set_blk_data(qup, msg);
|
||||||
|
|
||||||
|
blocks = qup->blk.count;
|
||||||
|
rem = msg->len - (blocks - 1) * limit;
|
||||||
|
|
||||||
if (msg->flags & I2C_M_RD) {
|
if (msg->flags & I2C_M_RD) {
|
||||||
rx_nents += (blocks * 2) + 1;
|
rx_nents += (blocks * 2) + 1;
|
||||||
tx_nents += 1;
|
tx_nents += 1;
|
||||||
|
|
||||||
while (qup->blk.pos < blocks) {
|
while (qup->blk.pos < blocks) {
|
||||||
/* length set to '0' implies 256 bytes */
|
tlen = (i == (blocks - 1)) ? rem : limit;
|
||||||
tlen = (i == (blocks - 1)) ? rem : 0;
|
|
||||||
tags = &qup->start_tag.start[off + len];
|
tags = &qup->start_tag.start[off + len];
|
||||||
len += qup_i2c_set_tags(tags, qup, msg, 1);
|
len += qup_i2c_set_tags(tags, qup, msg, 1);
|
||||||
|
qup->blk.data_len -= tlen;
|
||||||
|
|
||||||
/* scratch buf to read the start and len tags */
|
/* scratch buf to read the start and len tags */
|
||||||
ret = qup_sg_set_buf(&qup->brx.sg[rx_buf++],
|
ret = qup_sg_set_buf(&qup->brx.sg[rx_buf++],
|
||||||
&qup->brx.tag.start[0],
|
&qup->brx.tag.start[0],
|
||||||
&qup->brx.tag,
|
2, qup, DMA_FROM_DEVICE);
|
||||||
2, qup, 0, 0);
|
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = qup_sg_set_buf(&qup->brx.sg[rx_buf++],
|
ret = qup_sg_set_buf(&qup->brx.sg[rx_buf++],
|
||||||
&msg->buf[limit * i],
|
&msg->buf[limit * i],
|
||||||
NULL, tlen, qup,
|
tlen, qup,
|
||||||
1, DMA_FROM_DEVICE);
|
DMA_FROM_DEVICE);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -688,7 +698,7 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
|
||||||
}
|
}
|
||||||
ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++],
|
ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++],
|
||||||
&qup->start_tag.start[off],
|
&qup->start_tag.start[off],
|
||||||
&qup->start_tag, len, qup, 0, 0);
|
len, qup, DMA_TO_DEVICE);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -696,30 +706,28 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
|
||||||
/* scratch buf to read the BAM EOT and FLUSH tags */
|
/* scratch buf to read the BAM EOT and FLUSH tags */
|
||||||
ret = qup_sg_set_buf(&qup->brx.sg[rx_buf++],
|
ret = qup_sg_set_buf(&qup->brx.sg[rx_buf++],
|
||||||
&qup->brx.tag.start[0],
|
&qup->brx.tag.start[0],
|
||||||
&qup->brx.tag, 2,
|
2, qup, DMA_FROM_DEVICE);
|
||||||
qup, 0, 0);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
} else {
|
} else {
|
||||||
tx_nents += (blocks * 2);
|
tx_nents += (blocks * 2);
|
||||||
|
|
||||||
while (qup->blk.pos < blocks) {
|
while (qup->blk.pos < blocks) {
|
||||||
tlen = (i == (blocks - 1)) ? rem : 0;
|
tlen = (i == (blocks - 1)) ? rem : limit;
|
||||||
tags = &qup->start_tag.start[off + tx_len];
|
tags = &qup->start_tag.start[off + tx_len];
|
||||||
len = qup_i2c_set_tags(tags, qup, msg, 1);
|
len = qup_i2c_set_tags(tags, qup, msg, 1);
|
||||||
|
qup->blk.data_len -= tlen;
|
||||||
|
|
||||||
ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++],
|
ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++],
|
||||||
tags,
|
tags, len,
|
||||||
&qup->start_tag, len,
|
qup, DMA_TO_DEVICE);
|
||||||
qup, 0, 0);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
tx_len += len;
|
tx_len += len;
|
||||||
ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++],
|
ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++],
|
||||||
&msg->buf[limit * i],
|
&msg->buf[limit * i],
|
||||||
NULL, tlen, qup, 1,
|
tlen, qup, DMA_TO_DEVICE);
|
||||||
DMA_TO_DEVICE);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
i++;
|
i++;
|
||||||
|
@ -738,8 +746,7 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
|
||||||
QUP_BAM_FLUSH_STOP;
|
QUP_BAM_FLUSH_STOP;
|
||||||
ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++],
|
ret = qup_sg_set_buf(&qup->btx.sg[tx_buf++],
|
||||||
&qup->btx.tag.start[0],
|
&qup->btx.tag.start[0],
|
||||||
&qup->btx.tag, len,
|
len, qup, DMA_TO_DEVICE);
|
||||||
qup, 0, 0);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
tx_nents += 1;
|
tx_nents += 1;
|
||||||
|
@ -801,39 +808,35 @@ static int qup_i2c_bam_do_xfer(struct qup_i2c_dev *qup, struct i2c_msg *msg,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret || qup->bus_err || qup->qup_err) {
|
if (ret || qup->bus_err || qup->qup_err) {
|
||||||
if (qup->bus_err & QUP_I2C_NACK_FLAG) {
|
if (qup_i2c_change_state(qup, QUP_RUN_STATE)) {
|
||||||
msg--;
|
dev_err(qup->dev, "change to run state timed out");
|
||||||
dev_err(qup->dev, "NACK from %x\n", msg->addr);
|
goto desc_err;
|
||||||
ret = -EIO;
|
}
|
||||||
|
|
||||||
if (qup_i2c_change_state(qup, QUP_RUN_STATE)) {
|
if (rx_nents)
|
||||||
dev_err(qup->dev, "change to run state timed out");
|
writel(QUP_BAM_INPUT_EOT,
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rx_nents)
|
|
||||||
writel(QUP_BAM_INPUT_EOT,
|
|
||||||
qup->base + QUP_OUT_FIFO_BASE);
|
|
||||||
|
|
||||||
writel(QUP_BAM_FLUSH_STOP,
|
|
||||||
qup->base + QUP_OUT_FIFO_BASE);
|
qup->base + QUP_OUT_FIFO_BASE);
|
||||||
|
|
||||||
qup_i2c_flush(qup);
|
writel(QUP_BAM_FLUSH_STOP, qup->base + QUP_OUT_FIFO_BASE);
|
||||||
|
|
||||||
/* wait for remaining interrupts to occur */
|
qup_i2c_flush(qup);
|
||||||
if (!wait_for_completion_timeout(&qup->xfer, HZ))
|
|
||||||
dev_err(qup->dev, "flush timed out\n");
|
|
||||||
|
|
||||||
qup_i2c_rel_dma(qup);
|
/* wait for remaining interrupts to occur */
|
||||||
}
|
if (!wait_for_completion_timeout(&qup->xfer, HZ))
|
||||||
|
dev_err(qup->dev, "flush timed out\n");
|
||||||
|
|
||||||
|
qup_i2c_rel_dma(qup);
|
||||||
|
|
||||||
|
ret = (qup->bus_err & QUP_I2C_NACK_FLAG) ? -ENXIO : -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
desc_err:
|
||||||
dma_unmap_sg(qup->dev, qup->btx.sg, tx_nents, DMA_TO_DEVICE);
|
dma_unmap_sg(qup->dev, qup->btx.sg, tx_nents, DMA_TO_DEVICE);
|
||||||
|
|
||||||
if (rx_nents)
|
if (rx_nents)
|
||||||
dma_unmap_sg(qup->dev, qup->brx.sg, rx_nents,
|
dma_unmap_sg(qup->dev, qup->brx.sg, rx_nents,
|
||||||
DMA_FROM_DEVICE);
|
DMA_FROM_DEVICE);
|
||||||
desc_err:
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -849,9 +852,6 @@ static int qup_i2c_bam_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
qup->bus_err = 0;
|
|
||||||
qup->qup_err = 0;
|
|
||||||
|
|
||||||
writel(0, qup->base + QUP_MX_INPUT_CNT);
|
writel(0, qup->base + QUP_MX_INPUT_CNT);
|
||||||
writel(0, qup->base + QUP_MX_OUTPUT_CNT);
|
writel(0, qup->base + QUP_MX_OUTPUT_CNT);
|
||||||
|
|
||||||
|
@ -889,12 +889,8 @@ static int qup_i2c_wait_for_complete(struct qup_i2c_dev *qup,
|
||||||
ret = -ETIMEDOUT;
|
ret = -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (qup->bus_err || qup->qup_err) {
|
if (qup->bus_err || qup->qup_err)
|
||||||
if (qup->bus_err & QUP_I2C_NACK_FLAG) {
|
ret = (qup->bus_err & QUP_I2C_NACK_FLAG) ? -ENXIO : -EIO;
|
||||||
dev_err(qup->dev, "NACK from %x\n", msg->addr);
|
|
||||||
ret = -EIO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1020,7 +1016,7 @@ static void qup_i2c_issue_read(struct qup_i2c_dev *qup, struct i2c_msg *msg)
|
||||||
{
|
{
|
||||||
u32 addr, len, val;
|
u32 addr, len, val;
|
||||||
|
|
||||||
addr = (msg->addr << 1) | 1;
|
addr = i2c_8bit_addr_from_msg(msg);
|
||||||
|
|
||||||
/* 0 is used to specify a length 256 (QUP_READ_LIMIT) */
|
/* 0 is used to specify a length 256 (QUP_READ_LIMIT) */
|
||||||
len = (msg->len == QUP_READ_LIMIT) ? 0 : msg->len;
|
len = (msg->len == QUP_READ_LIMIT) ? 0 : msg->len;
|
||||||
|
@ -1186,6 +1182,9 @@ static int qup_i2c_xfer(struct i2c_adapter *adap,
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
qup->bus_err = 0;
|
||||||
|
qup->qup_err = 0;
|
||||||
|
|
||||||
writel(1, qup->base + QUP_SW_RESET);
|
writel(1, qup->base + QUP_SW_RESET);
|
||||||
ret = qup_i2c_poll_state(qup, QUP_RESET_STATE);
|
ret = qup_i2c_poll_state(qup, QUP_RESET_STATE);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -1235,6 +1234,9 @@ static int qup_i2c_xfer_v2(struct i2c_adapter *adap,
|
||||||
struct qup_i2c_dev *qup = i2c_get_adapdata(adap);
|
struct qup_i2c_dev *qup = i2c_get_adapdata(adap);
|
||||||
int ret, len, idx = 0, use_dma = 0;
|
int ret, len, idx = 0, use_dma = 0;
|
||||||
|
|
||||||
|
qup->bus_err = 0;
|
||||||
|
qup->qup_err = 0;
|
||||||
|
|
||||||
ret = pm_runtime_get_sync(qup->dev);
|
ret = pm_runtime_get_sync(qup->dev);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -1409,27 +1411,21 @@ static int qup_i2c_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
/* 2 tag bytes for each block + 5 for start, stop tags */
|
/* 2 tag bytes for each block + 5 for start, stop tags */
|
||||||
size = blocks * 2 + 5;
|
size = blocks * 2 + 5;
|
||||||
qup->dpool = dma_pool_create("qup_i2c-dma-pool", &pdev->dev,
|
|
||||||
size, 4, 0);
|
|
||||||
|
|
||||||
qup->start_tag.start = dma_pool_alloc(qup->dpool, GFP_KERNEL,
|
qup->start_tag.start = devm_kzalloc(&pdev->dev,
|
||||||
&qup->start_tag.addr);
|
size, GFP_KERNEL);
|
||||||
if (!qup->start_tag.start) {
|
if (!qup->start_tag.start) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto fail_dma;
|
goto fail_dma;
|
||||||
}
|
}
|
||||||
|
|
||||||
qup->brx.tag.start = dma_pool_alloc(qup->dpool,
|
qup->brx.tag.start = devm_kzalloc(&pdev->dev, 2, GFP_KERNEL);
|
||||||
GFP_KERNEL,
|
|
||||||
&qup->brx.tag.addr);
|
|
||||||
if (!qup->brx.tag.start) {
|
if (!qup->brx.tag.start) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto fail_dma;
|
goto fail_dma;
|
||||||
}
|
}
|
||||||
|
|
||||||
qup->btx.tag.start = dma_pool_alloc(qup->dpool,
|
qup->btx.tag.start = devm_kzalloc(&pdev->dev, 2, GFP_KERNEL);
|
||||||
GFP_KERNEL,
|
|
||||||
&qup->btx.tag.addr);
|
|
||||||
if (!qup->btx.tag.start) {
|
if (!qup->btx.tag.start) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto fail_dma;
|
goto fail_dma;
|
||||||
|
@ -1568,13 +1564,6 @@ static int qup_i2c_remove(struct platform_device *pdev)
|
||||||
struct qup_i2c_dev *qup = platform_get_drvdata(pdev);
|
struct qup_i2c_dev *qup = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
if (qup->is_dma) {
|
if (qup->is_dma) {
|
||||||
dma_pool_free(qup->dpool, qup->start_tag.start,
|
|
||||||
qup->start_tag.addr);
|
|
||||||
dma_pool_free(qup->dpool, qup->brx.tag.start,
|
|
||||||
qup->brx.tag.addr);
|
|
||||||
dma_pool_free(qup->dpool, qup->btx.tag.start,
|
|
||||||
qup->btx.tag.addr);
|
|
||||||
dma_pool_destroy(qup->dpool);
|
|
||||||
dma_release_channel(qup->btx.dma);
|
dma_release_channel(qup->btx.dma);
|
||||||
dma_release_channel(qup->brx.dma);
|
dma_release_channel(qup->brx.dma);
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,6 +58,12 @@ enum {
|
||||||
#define REG_CON_LASTACK BIT(5) /* 1: send NACK after last received byte */
|
#define REG_CON_LASTACK BIT(5) /* 1: send NACK after last received byte */
|
||||||
#define REG_CON_ACTACK BIT(6) /* 1: stop if NACK is received */
|
#define REG_CON_ACTACK BIT(6) /* 1: stop if NACK is received */
|
||||||
|
|
||||||
|
#define REG_CON_TUNING_MASK GENMASK(15, 8)
|
||||||
|
|
||||||
|
#define REG_CON_SDA_CFG(cfg) ((cfg) << 8)
|
||||||
|
#define REG_CON_STA_CFG(cfg) ((cfg) << 12)
|
||||||
|
#define REG_CON_STO_CFG(cfg) ((cfg) << 14)
|
||||||
|
|
||||||
/* REG_MRXADDR bits */
|
/* REG_MRXADDR bits */
|
||||||
#define REG_MRXADDR_VALID(x) BIT(24 + (x)) /* [x*8+7:x*8] of MRX[R]ADDR valid */
|
#define REG_MRXADDR_VALID(x) BIT(24 + (x)) /* [x*8+7:x*8] of MRX[R]ADDR valid */
|
||||||
|
|
||||||
|
@ -75,6 +81,77 @@ enum {
|
||||||
#define WAIT_TIMEOUT 1000 /* ms */
|
#define WAIT_TIMEOUT 1000 /* ms */
|
||||||
#define DEFAULT_SCL_RATE (100 * 1000) /* Hz */
|
#define DEFAULT_SCL_RATE (100 * 1000) /* Hz */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct i2c_spec_values:
|
||||||
|
* @min_hold_start_ns: min hold time (repeated) START condition
|
||||||
|
* @min_low_ns: min LOW period of the SCL clock
|
||||||
|
* @min_high_ns: min HIGH period of the SCL cloc
|
||||||
|
* @min_setup_start_ns: min set-up time for a repeated START conditio
|
||||||
|
* @max_data_hold_ns: max data hold time
|
||||||
|
* @min_data_setup_ns: min data set-up time
|
||||||
|
* @min_setup_stop_ns: min set-up time for STOP condition
|
||||||
|
* @min_hold_buffer_ns: min bus free time between a STOP and
|
||||||
|
* START condition
|
||||||
|
*/
|
||||||
|
struct i2c_spec_values {
|
||||||
|
unsigned long min_hold_start_ns;
|
||||||
|
unsigned long min_low_ns;
|
||||||
|
unsigned long min_high_ns;
|
||||||
|
unsigned long min_setup_start_ns;
|
||||||
|
unsigned long max_data_hold_ns;
|
||||||
|
unsigned long min_data_setup_ns;
|
||||||
|
unsigned long min_setup_stop_ns;
|
||||||
|
unsigned long min_hold_buffer_ns;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct i2c_spec_values standard_mode_spec = {
|
||||||
|
.min_hold_start_ns = 4000,
|
||||||
|
.min_low_ns = 4700,
|
||||||
|
.min_high_ns = 4000,
|
||||||
|
.min_setup_start_ns = 4700,
|
||||||
|
.max_data_hold_ns = 3450,
|
||||||
|
.min_data_setup_ns = 250,
|
||||||
|
.min_setup_stop_ns = 4000,
|
||||||
|
.min_hold_buffer_ns = 4700,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct i2c_spec_values fast_mode_spec = {
|
||||||
|
.min_hold_start_ns = 600,
|
||||||
|
.min_low_ns = 1300,
|
||||||
|
.min_high_ns = 600,
|
||||||
|
.min_setup_start_ns = 600,
|
||||||
|
.max_data_hold_ns = 900,
|
||||||
|
.min_data_setup_ns = 100,
|
||||||
|
.min_setup_stop_ns = 600,
|
||||||
|
.min_hold_buffer_ns = 1300,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct i2c_spec_values fast_mode_plus_spec = {
|
||||||
|
.min_hold_start_ns = 260,
|
||||||
|
.min_low_ns = 500,
|
||||||
|
.min_high_ns = 260,
|
||||||
|
.min_setup_start_ns = 260,
|
||||||
|
.max_data_hold_ns = 400,
|
||||||
|
.min_data_setup_ns = 50,
|
||||||
|
.min_setup_stop_ns = 260,
|
||||||
|
.min_hold_buffer_ns = 500,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct rk3x_i2c_calced_timings:
|
||||||
|
* @div_low: Divider output for low
|
||||||
|
* @div_high: Divider output for high
|
||||||
|
* @tuning: Used to adjust setup/hold data time,
|
||||||
|
* setup/hold start time and setup stop time for
|
||||||
|
* v1's calc_timings, the tuning should all be 0
|
||||||
|
* for old hardware anyone using v0's calc_timings.
|
||||||
|
*/
|
||||||
|
struct rk3x_i2c_calced_timings {
|
||||||
|
unsigned long div_low;
|
||||||
|
unsigned long div_high;
|
||||||
|
unsigned int tuning;
|
||||||
|
};
|
||||||
|
|
||||||
enum rk3x_i2c_state {
|
enum rk3x_i2c_state {
|
||||||
STATE_IDLE,
|
STATE_IDLE,
|
||||||
STATE_START,
|
STATE_START,
|
||||||
|
@ -85,11 +162,35 @@ enum rk3x_i2c_state {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @grf_offset: offset inside the grf regmap for setting the i2c type
|
* @grf_offset: offset inside the grf regmap for setting the i2c type
|
||||||
|
* @calc_timings: Callback function for i2c timing information calculated
|
||||||
*/
|
*/
|
||||||
struct rk3x_i2c_soc_data {
|
struct rk3x_i2c_soc_data {
|
||||||
int grf_offset;
|
int grf_offset;
|
||||||
|
int (*calc_timings)(unsigned long, struct i2c_timings *,
|
||||||
|
struct rk3x_i2c_calced_timings *);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct rk3x_i2c - private data of the controller
|
||||||
|
* @adap: corresponding I2C adapter
|
||||||
|
* @dev: device for this controller
|
||||||
|
* @soc_data: related soc data struct
|
||||||
|
* @regs: virtual memory area
|
||||||
|
* @clk: function clk for rk3399 or function & Bus clks for others
|
||||||
|
* @pclk: Bus clk for rk3399
|
||||||
|
* @clk_rate_nb: i2c clk rate change notify
|
||||||
|
* @t: I2C known timing information
|
||||||
|
* @lock: spinlock for the i2c bus
|
||||||
|
* @wait: the waitqueue to wait for i2c transfer
|
||||||
|
* @busy: the condition for the event to wait for
|
||||||
|
* @msg: current i2c message
|
||||||
|
* @addr: addr of i2c slave device
|
||||||
|
* @mode: mode of i2c transfer
|
||||||
|
* @is_last_msg: flag determines whether it is the last msg in this transfer
|
||||||
|
* @state: state of i2c transfer
|
||||||
|
* @processed: byte length which has been send or received
|
||||||
|
* @error: error code for i2c transfer
|
||||||
|
*/
|
||||||
struct rk3x_i2c {
|
struct rk3x_i2c {
|
||||||
struct i2c_adapter adap;
|
struct i2c_adapter adap;
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
|
@ -98,6 +199,7 @@ struct rk3x_i2c {
|
||||||
/* Hardware resources */
|
/* Hardware resources */
|
||||||
void __iomem *regs;
|
void __iomem *regs;
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
|
struct clk *pclk;
|
||||||
struct notifier_block clk_rate_nb;
|
struct notifier_block clk_rate_nb;
|
||||||
|
|
||||||
/* Settings */
|
/* Settings */
|
||||||
|
@ -116,7 +218,7 @@ struct rk3x_i2c {
|
||||||
|
|
||||||
/* I2C state machine */
|
/* I2C state machine */
|
||||||
enum rk3x_i2c_state state;
|
enum rk3x_i2c_state state;
|
||||||
unsigned int processed; /* sent/received bytes */
|
unsigned int processed;
|
||||||
int error;
|
int error;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -142,13 +244,12 @@ static inline void rk3x_i2c_clean_ipd(struct rk3x_i2c *i2c)
|
||||||
*/
|
*/
|
||||||
static void rk3x_i2c_start(struct rk3x_i2c *i2c)
|
static void rk3x_i2c_start(struct rk3x_i2c *i2c)
|
||||||
{
|
{
|
||||||
u32 val;
|
u32 val = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK;
|
||||||
|
|
||||||
rk3x_i2c_clean_ipd(i2c);
|
|
||||||
i2c_writel(i2c, REG_INT_START, REG_IEN);
|
i2c_writel(i2c, REG_INT_START, REG_IEN);
|
||||||
|
|
||||||
/* enable adapter with correct mode, send START condition */
|
/* enable adapter with correct mode, send START condition */
|
||||||
val = REG_CON_EN | REG_CON_MOD(i2c->mode) | REG_CON_START;
|
val |= REG_CON_EN | REG_CON_MOD(i2c->mode) | REG_CON_START;
|
||||||
|
|
||||||
/* if we want to react to NACK, set ACTACK bit */
|
/* if we want to react to NACK, set ACTACK bit */
|
||||||
if (!(i2c->msg->flags & I2C_M_IGNORE_NAK))
|
if (!(i2c->msg->flags & I2C_M_IGNORE_NAK))
|
||||||
|
@ -189,7 +290,8 @@ static void rk3x_i2c_stop(struct rk3x_i2c *i2c, int error)
|
||||||
* get the intended effect by resetting its internal state
|
* get the intended effect by resetting its internal state
|
||||||
* and issuing an ordinary START.
|
* and issuing an ordinary START.
|
||||||
*/
|
*/
|
||||||
i2c_writel(i2c, 0, REG_CON);
|
ctrl = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK;
|
||||||
|
i2c_writel(i2c, ctrl, REG_CON);
|
||||||
|
|
||||||
/* signal that we are finished with the current msg */
|
/* signal that we are finished with the current msg */
|
||||||
wake_up(&i2c->wait);
|
wake_up(&i2c->wait);
|
||||||
|
@ -430,27 +532,38 @@ out:
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get timing values of I2C specification
|
||||||
|
*
|
||||||
|
* @speed: Desired SCL frequency
|
||||||
|
*
|
||||||
|
* Returns: Matched i2c spec values.
|
||||||
|
*/
|
||||||
|
static const struct i2c_spec_values *rk3x_i2c_get_spec(unsigned int speed)
|
||||||
|
{
|
||||||
|
if (speed <= 100000)
|
||||||
|
return &standard_mode_spec;
|
||||||
|
else if (speed <= 400000)
|
||||||
|
return &fast_mode_spec;
|
||||||
|
else
|
||||||
|
return &fast_mode_plus_spec;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate divider values for desired SCL frequency
|
* Calculate divider values for desired SCL frequency
|
||||||
*
|
*
|
||||||
* @clk_rate: I2C input clock rate
|
* @clk_rate: I2C input clock rate
|
||||||
* @t: Known I2C timing information.
|
* @t: Known I2C timing information
|
||||||
* @div_low: Divider output for low
|
* @t_calc: Caculated rk3x private timings that would be written into regs
|
||||||
* @div_high: Divider output for high
|
|
||||||
*
|
*
|
||||||
* Returns: 0 on success, -EINVAL if the goal SCL rate is too slow. In that case
|
* Returns: 0 on success, -EINVAL if the goal SCL rate is too slow. In that case
|
||||||
* a best-effort divider value is returned in divs. If the target rate is
|
* a best-effort divider value is returned in divs. If the target rate is
|
||||||
* too high, we silently use the highest possible rate.
|
* too high, we silently use the highest possible rate.
|
||||||
*/
|
*/
|
||||||
static int rk3x_i2c_calc_divs(unsigned long clk_rate,
|
static int rk3x_i2c_v0_calc_timings(unsigned long clk_rate,
|
||||||
struct i2c_timings *t,
|
struct i2c_timings *t,
|
||||||
unsigned long *div_low,
|
struct rk3x_i2c_calced_timings *t_calc)
|
||||||
unsigned long *div_high)
|
|
||||||
{
|
{
|
||||||
unsigned long spec_min_low_ns, spec_min_high_ns;
|
|
||||||
unsigned long spec_setup_start, spec_max_data_hold_ns;
|
|
||||||
unsigned long data_hold_buffer_ns;
|
|
||||||
|
|
||||||
unsigned long min_low_ns, min_high_ns;
|
unsigned long min_low_ns, min_high_ns;
|
||||||
unsigned long max_low_ns, min_total_ns;
|
unsigned long max_low_ns, min_total_ns;
|
||||||
|
|
||||||
|
@ -462,6 +575,8 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate,
|
||||||
unsigned long min_div_for_hold, min_total_div;
|
unsigned long min_div_for_hold, min_total_div;
|
||||||
unsigned long extra_div, extra_low_div, ideal_low_div;
|
unsigned long extra_div, extra_low_div, ideal_low_div;
|
||||||
|
|
||||||
|
unsigned long data_hold_buffer_ns = 50;
|
||||||
|
const struct i2c_spec_values *spec;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
/* Only support standard-mode and fast-mode */
|
/* Only support standard-mode and fast-mode */
|
||||||
|
@ -484,22 +599,8 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate,
|
||||||
* This is because the i2c host on Rockchip holds the data line
|
* This is because the i2c host on Rockchip holds the data line
|
||||||
* for half the low time.
|
* for half the low time.
|
||||||
*/
|
*/
|
||||||
if (t->bus_freq_hz <= 100000) {
|
spec = rk3x_i2c_get_spec(t->bus_freq_hz);
|
||||||
/* Standard-mode */
|
min_high_ns = t->scl_rise_ns + spec->min_high_ns;
|
||||||
spec_min_low_ns = 4700;
|
|
||||||
spec_setup_start = 4700;
|
|
||||||
spec_min_high_ns = 4000;
|
|
||||||
spec_max_data_hold_ns = 3450;
|
|
||||||
data_hold_buffer_ns = 50;
|
|
||||||
} else {
|
|
||||||
/* Fast-mode */
|
|
||||||
spec_min_low_ns = 1300;
|
|
||||||
spec_setup_start = 600;
|
|
||||||
spec_min_high_ns = 600;
|
|
||||||
spec_max_data_hold_ns = 900;
|
|
||||||
data_hold_buffer_ns = 50;
|
|
||||||
}
|
|
||||||
min_high_ns = t->scl_rise_ns + spec_min_high_ns;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Timings for repeated start:
|
* Timings for repeated start:
|
||||||
|
@ -509,14 +610,14 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate,
|
||||||
* We need to account for those rules in picking our "high" time so
|
* We need to account for those rules in picking our "high" time so
|
||||||
* we meet tSU;STA and tHD;STA times.
|
* we meet tSU;STA and tHD;STA times.
|
||||||
*/
|
*/
|
||||||
min_high_ns = max(min_high_ns,
|
min_high_ns = max(min_high_ns, DIV_ROUND_UP(
|
||||||
DIV_ROUND_UP((t->scl_rise_ns + spec_setup_start) * 1000, 875));
|
(t->scl_rise_ns + spec->min_setup_start_ns) * 1000, 875));
|
||||||
min_high_ns = max(min_high_ns,
|
min_high_ns = max(min_high_ns, DIV_ROUND_UP(
|
||||||
DIV_ROUND_UP((t->scl_rise_ns + spec_setup_start +
|
(t->scl_rise_ns + spec->min_setup_start_ns + t->sda_fall_ns +
|
||||||
t->sda_fall_ns + spec_min_high_ns), 2));
|
spec->min_high_ns), 2));
|
||||||
|
|
||||||
min_low_ns = t->scl_fall_ns + spec_min_low_ns;
|
min_low_ns = t->scl_fall_ns + spec->min_low_ns;
|
||||||
max_low_ns = spec_max_data_hold_ns * 2 - data_hold_buffer_ns;
|
max_low_ns = spec->max_data_hold_ns * 2 - data_hold_buffer_ns;
|
||||||
min_total_ns = min_low_ns + min_high_ns;
|
min_total_ns = min_low_ns + min_high_ns;
|
||||||
|
|
||||||
/* Adjust to avoid overflow */
|
/* Adjust to avoid overflow */
|
||||||
|
@ -552,8 +653,8 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate,
|
||||||
* Time needed to meet hold requirements is important.
|
* Time needed to meet hold requirements is important.
|
||||||
* Just use that.
|
* Just use that.
|
||||||
*/
|
*/
|
||||||
*div_low = min_low_div;
|
t_calc->div_low = min_low_div;
|
||||||
*div_high = min_high_div;
|
t_calc->div_high = min_high_div;
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* We've got to distribute some time among the low and high
|
* We've got to distribute some time among the low and high
|
||||||
|
@ -582,25 +683,186 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate,
|
||||||
|
|
||||||
/* Give low the "ideal" and give high whatever extra is left */
|
/* Give low the "ideal" and give high whatever extra is left */
|
||||||
extra_low_div = ideal_low_div - min_low_div;
|
extra_low_div = ideal_low_div - min_low_div;
|
||||||
*div_low = ideal_low_div;
|
t_calc->div_low = ideal_low_div;
|
||||||
*div_high = min_high_div + (extra_div - extra_low_div);
|
t_calc->div_high = min_high_div + (extra_div - extra_low_div);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Adjust to the fact that the hardware has an implicit "+1".
|
* Adjust to the fact that the hardware has an implicit "+1".
|
||||||
* NOTE: Above calculations always produce div_low > 0 and div_high > 0.
|
* NOTE: Above calculations always produce div_low > 0 and div_high > 0.
|
||||||
*/
|
*/
|
||||||
*div_low = *div_low - 1;
|
t_calc->div_low--;
|
||||||
*div_high = *div_high - 1;
|
t_calc->div_high--;
|
||||||
|
|
||||||
/* Maximum divider supported by hw is 0xffff */
|
/* Maximum divider supported by hw is 0xffff */
|
||||||
if (*div_low > 0xffff) {
|
if (t_calc->div_low > 0xffff) {
|
||||||
*div_low = 0xffff;
|
t_calc->div_low = 0xffff;
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*div_high > 0xffff) {
|
if (t_calc->div_high > 0xffff) {
|
||||||
*div_high = 0xffff;
|
t_calc->div_high = 0xffff;
|
||||||
|
ret = -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate timing values for desired SCL frequency
|
||||||
|
*
|
||||||
|
* @clk_rate: I2C input clock rate
|
||||||
|
* @t: Known I2C timing information
|
||||||
|
* @t_calc: Caculated rk3x private timings that would be written into regs
|
||||||
|
*
|
||||||
|
* Returns: 0 on success, -EINVAL if the goal SCL rate is too slow. In that case
|
||||||
|
* a best-effort divider value is returned in divs. If the target rate is
|
||||||
|
* too high, we silently use the highest possible rate.
|
||||||
|
* The following formulas are v1's method to calculate timings.
|
||||||
|
*
|
||||||
|
* l = divl + 1;
|
||||||
|
* h = divh + 1;
|
||||||
|
* s = sda_update_config + 1;
|
||||||
|
* u = start_setup_config + 1;
|
||||||
|
* p = stop_setup_config + 1;
|
||||||
|
* T = Tclk_i2c;
|
||||||
|
*
|
||||||
|
* tHigh = 8 * h * T;
|
||||||
|
* tLow = 8 * l * T;
|
||||||
|
*
|
||||||
|
* tHD;sda = (l * s + 1) * T;
|
||||||
|
* tSU;sda = [(8 - s) * l + 1] * T;
|
||||||
|
* tI2C = 8 * (l + h) * T;
|
||||||
|
*
|
||||||
|
* tSU;sta = (8h * u + 1) * T;
|
||||||
|
* tHD;sta = [8h * (u + 1) - 1] * T;
|
||||||
|
* tSU;sto = (8h * p + 1) * T;
|
||||||
|
*/
|
||||||
|
static int rk3x_i2c_v1_calc_timings(unsigned long clk_rate,
|
||||||
|
struct i2c_timings *t,
|
||||||
|
struct rk3x_i2c_calced_timings *t_calc)
|
||||||
|
{
|
||||||
|
unsigned long min_low_ns, min_high_ns, min_total_ns;
|
||||||
|
unsigned long min_setup_start_ns, min_setup_data_ns;
|
||||||
|
unsigned long min_setup_stop_ns, max_hold_data_ns;
|
||||||
|
|
||||||
|
unsigned long clk_rate_khz, scl_rate_khz;
|
||||||
|
|
||||||
|
unsigned long min_low_div, min_high_div;
|
||||||
|
|
||||||
|
unsigned long min_div_for_hold, min_total_div;
|
||||||
|
unsigned long extra_div, extra_low_div;
|
||||||
|
unsigned long sda_update_cfg, stp_sta_cfg, stp_sto_cfg;
|
||||||
|
|
||||||
|
const struct i2c_spec_values *spec;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
/* Support standard-mode, fast-mode and fast-mode plus */
|
||||||
|
if (WARN_ON(t->bus_freq_hz > 1000000))
|
||||||
|
t->bus_freq_hz = 1000000;
|
||||||
|
|
||||||
|
/* prevent scl_rate_khz from becoming 0 */
|
||||||
|
if (WARN_ON(t->bus_freq_hz < 1000))
|
||||||
|
t->bus_freq_hz = 1000;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* min_low_ns: The minimum number of ns we need to hold low to
|
||||||
|
* meet I2C specification, should include fall time.
|
||||||
|
* min_high_ns: The minimum number of ns we need to hold high to
|
||||||
|
* meet I2C specification, should include rise time.
|
||||||
|
*/
|
||||||
|
spec = rk3x_i2c_get_spec(t->bus_freq_hz);
|
||||||
|
|
||||||
|
/* calculate min-divh and min-divl */
|
||||||
|
clk_rate_khz = DIV_ROUND_UP(clk_rate, 1000);
|
||||||
|
scl_rate_khz = t->bus_freq_hz / 1000;
|
||||||
|
min_total_div = DIV_ROUND_UP(clk_rate_khz, scl_rate_khz * 8);
|
||||||
|
|
||||||
|
min_high_ns = t->scl_rise_ns + spec->min_high_ns;
|
||||||
|
min_high_div = DIV_ROUND_UP(clk_rate_khz * min_high_ns, 8 * 1000000);
|
||||||
|
|
||||||
|
min_low_ns = t->scl_fall_ns + spec->min_low_ns;
|
||||||
|
min_low_div = DIV_ROUND_UP(clk_rate_khz * min_low_ns, 8 * 1000000);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Final divh and divl must be greater than 0, otherwise the
|
||||||
|
* hardware would not output the i2c clk.
|
||||||
|
*/
|
||||||
|
min_high_div = (min_high_div < 1) ? 2 : min_high_div;
|
||||||
|
min_low_div = (min_low_div < 1) ? 2 : min_low_div;
|
||||||
|
|
||||||
|
/* These are the min dividers needed for min hold times. */
|
||||||
|
min_div_for_hold = (min_low_div + min_high_div);
|
||||||
|
min_total_ns = min_low_ns + min_high_ns;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is the maximum divider so we don't go over the maximum.
|
||||||
|
* We don't round up here (we round down) since this is a maximum.
|
||||||
|
*/
|
||||||
|
if (min_div_for_hold >= min_total_div) {
|
||||||
|
/*
|
||||||
|
* Time needed to meet hold requirements is important.
|
||||||
|
* Just use that.
|
||||||
|
*/
|
||||||
|
t_calc->div_low = min_low_div;
|
||||||
|
t_calc->div_high = min_high_div;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* We've got to distribute some time among the low and high
|
||||||
|
* so we don't run too fast.
|
||||||
|
* We'll try to split things up by the scale of min_low_div and
|
||||||
|
* min_high_div, biasing slightly towards having a higher div
|
||||||
|
* for low (spend more time low).
|
||||||
|
*/
|
||||||
|
extra_div = min_total_div - min_div_for_hold;
|
||||||
|
extra_low_div = DIV_ROUND_UP(min_low_div * extra_div,
|
||||||
|
min_div_for_hold);
|
||||||
|
|
||||||
|
t_calc->div_low = min_low_div + extra_low_div;
|
||||||
|
t_calc->div_high = min_high_div + (extra_div - extra_low_div);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* calculate sda data hold count by the rules, data_upd_st:3
|
||||||
|
* is a appropriate value to reduce calculated times.
|
||||||
|
*/
|
||||||
|
for (sda_update_cfg = 3; sda_update_cfg > 0; sda_update_cfg--) {
|
||||||
|
max_hold_data_ns = DIV_ROUND_UP((sda_update_cfg
|
||||||
|
* (t_calc->div_low) + 1)
|
||||||
|
* 1000000, clk_rate_khz);
|
||||||
|
min_setup_data_ns = DIV_ROUND_UP(((8 - sda_update_cfg)
|
||||||
|
* (t_calc->div_low) + 1)
|
||||||
|
* 1000000, clk_rate_khz);
|
||||||
|
if ((max_hold_data_ns < spec->max_data_hold_ns) &&
|
||||||
|
(min_setup_data_ns > spec->min_data_setup_ns))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* calculate setup start config */
|
||||||
|
min_setup_start_ns = t->scl_rise_ns + spec->min_setup_start_ns;
|
||||||
|
stp_sta_cfg = DIV_ROUND_UP(clk_rate_khz * min_setup_start_ns
|
||||||
|
- 1000000, 8 * 1000000 * (t_calc->div_high));
|
||||||
|
|
||||||
|
/* calculate setup stop config */
|
||||||
|
min_setup_stop_ns = t->scl_rise_ns + spec->min_setup_stop_ns;
|
||||||
|
stp_sto_cfg = DIV_ROUND_UP(clk_rate_khz * min_setup_stop_ns
|
||||||
|
- 1000000, 8 * 1000000 * (t_calc->div_high));
|
||||||
|
|
||||||
|
t_calc->tuning = REG_CON_SDA_CFG(--sda_update_cfg) |
|
||||||
|
REG_CON_STA_CFG(--stp_sta_cfg) |
|
||||||
|
REG_CON_STO_CFG(--stp_sto_cfg);
|
||||||
|
|
||||||
|
t_calc->div_low--;
|
||||||
|
t_calc->div_high--;
|
||||||
|
|
||||||
|
/* Maximum divider supported by hw is 0xffff */
|
||||||
|
if (t_calc->div_low > 0xffff) {
|
||||||
|
t_calc->div_low = 0xffff;
|
||||||
|
ret = -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t_calc->div_high > 0xffff) {
|
||||||
|
t_calc->div_high = 0xffff;
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -610,19 +872,31 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate,
|
||||||
static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, unsigned long clk_rate)
|
static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, unsigned long clk_rate)
|
||||||
{
|
{
|
||||||
struct i2c_timings *t = &i2c->t;
|
struct i2c_timings *t = &i2c->t;
|
||||||
unsigned long div_low, div_high;
|
struct rk3x_i2c_calced_timings calc;
|
||||||
u64 t_low_ns, t_high_ns;
|
u64 t_low_ns, t_high_ns;
|
||||||
|
unsigned long flags;
|
||||||
|
u32 val;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = rk3x_i2c_calc_divs(clk_rate, t, &div_low, &div_high);
|
ret = i2c->soc_data->calc_timings(clk_rate, t, &calc);
|
||||||
WARN_ONCE(ret != 0, "Could not reach SCL freq %u", t->bus_freq_hz);
|
WARN_ONCE(ret != 0, "Could not reach SCL freq %u", t->bus_freq_hz);
|
||||||
|
|
||||||
clk_enable(i2c->clk);
|
clk_enable(i2c->pclk);
|
||||||
i2c_writel(i2c, (div_high << 16) | (div_low & 0xffff), REG_CLKDIV);
|
|
||||||
clk_disable(i2c->clk);
|
|
||||||
|
|
||||||
t_low_ns = div_u64(((u64)div_low + 1) * 8 * 1000000000, clk_rate);
|
spin_lock_irqsave(&i2c->lock, flags);
|
||||||
t_high_ns = div_u64(((u64)div_high + 1) * 8 * 1000000000, clk_rate);
|
val = i2c_readl(i2c, REG_CON);
|
||||||
|
val &= ~REG_CON_TUNING_MASK;
|
||||||
|
val |= calc.tuning;
|
||||||
|
i2c_writel(i2c, val, REG_CON);
|
||||||
|
i2c_writel(i2c, (calc.div_high << 16) | (calc.div_low & 0xffff),
|
||||||
|
REG_CLKDIV);
|
||||||
|
spin_unlock_irqrestore(&i2c->lock, flags);
|
||||||
|
|
||||||
|
clk_disable(i2c->pclk);
|
||||||
|
|
||||||
|
t_low_ns = div_u64(((u64)calc.div_low + 1) * 8 * 1000000000, clk_rate);
|
||||||
|
t_high_ns = div_u64(((u64)calc.div_high + 1) * 8 * 1000000000,
|
||||||
|
clk_rate);
|
||||||
dev_dbg(i2c->dev,
|
dev_dbg(i2c->dev,
|
||||||
"CLK %lukhz, Req %uns, Act low %lluns high %lluns\n",
|
"CLK %lukhz, Req %uns, Act low %lluns high %lluns\n",
|
||||||
clk_rate / 1000,
|
clk_rate / 1000,
|
||||||
|
@ -652,12 +926,17 @@ static int rk3x_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long
|
||||||
{
|
{
|
||||||
struct clk_notifier_data *ndata = data;
|
struct clk_notifier_data *ndata = data;
|
||||||
struct rk3x_i2c *i2c = container_of(nb, struct rk3x_i2c, clk_rate_nb);
|
struct rk3x_i2c *i2c = container_of(nb, struct rk3x_i2c, clk_rate_nb);
|
||||||
unsigned long div_low, div_high;
|
struct rk3x_i2c_calced_timings calc;
|
||||||
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case PRE_RATE_CHANGE:
|
case PRE_RATE_CHANGE:
|
||||||
if (rk3x_i2c_calc_divs(ndata->new_rate, &i2c->t,
|
/*
|
||||||
&div_low, &div_high) != 0)
|
* Try the calculation (but don't store the result) ahead of
|
||||||
|
* time to see if we need to block the clock change. Timings
|
||||||
|
* shouldn't actually take effect until rk3x_i2c_adapt_div().
|
||||||
|
*/
|
||||||
|
if (i2c->soc_data->calc_timings(ndata->new_rate, &i2c->t,
|
||||||
|
&calc) != 0)
|
||||||
return NOTIFY_STOP;
|
return NOTIFY_STOP;
|
||||||
|
|
||||||
/* scale up */
|
/* scale up */
|
||||||
|
@ -767,12 +1046,14 @@ static int rk3x_i2c_xfer(struct i2c_adapter *adap,
|
||||||
{
|
{
|
||||||
struct rk3x_i2c *i2c = (struct rk3x_i2c *)adap->algo_data;
|
struct rk3x_i2c *i2c = (struct rk3x_i2c *)adap->algo_data;
|
||||||
unsigned long timeout, flags;
|
unsigned long timeout, flags;
|
||||||
|
u32 val;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
spin_lock_irqsave(&i2c->lock, flags);
|
spin_lock_irqsave(&i2c->lock, flags);
|
||||||
|
|
||||||
clk_enable(i2c->clk);
|
clk_enable(i2c->clk);
|
||||||
|
clk_enable(i2c->pclk);
|
||||||
|
|
||||||
i2c->is_last_msg = false;
|
i2c->is_last_msg = false;
|
||||||
|
|
||||||
|
@ -806,7 +1087,9 @@ static int rk3x_i2c_xfer(struct i2c_adapter *adap,
|
||||||
|
|
||||||
/* Force a STOP condition without interrupt */
|
/* Force a STOP condition without interrupt */
|
||||||
i2c_writel(i2c, 0, REG_IEN);
|
i2c_writel(i2c, 0, REG_IEN);
|
||||||
i2c_writel(i2c, REG_CON_EN | REG_CON_STOP, REG_CON);
|
val = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK;
|
||||||
|
val |= REG_CON_EN | REG_CON_STOP;
|
||||||
|
i2c_writel(i2c, val, REG_CON);
|
||||||
|
|
||||||
i2c->state = STATE_IDLE;
|
i2c->state = STATE_IDLE;
|
||||||
|
|
||||||
|
@ -820,7 +1103,9 @@ static int rk3x_i2c_xfer(struct i2c_adapter *adap,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clk_disable(i2c->pclk);
|
||||||
clk_disable(i2c->clk);
|
clk_disable(i2c->clk);
|
||||||
|
|
||||||
spin_unlock_irqrestore(&i2c->lock, flags);
|
spin_unlock_irqrestore(&i2c->lock, flags);
|
||||||
|
|
||||||
return ret < 0 ? ret : num;
|
return ret < 0 ? ret : num;
|
||||||
|
@ -836,17 +1121,52 @@ static const struct i2c_algorithm rk3x_i2c_algorithm = {
|
||||||
.functionality = rk3x_i2c_func,
|
.functionality = rk3x_i2c_func,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct rk3x_i2c_soc_data soc_data[3] = {
|
static const struct rk3x_i2c_soc_data rk3066_soc_data = {
|
||||||
{ .grf_offset = 0x154 }, /* rk3066 */
|
.grf_offset = 0x154,
|
||||||
{ .grf_offset = 0x0a4 }, /* rk3188 */
|
.calc_timings = rk3x_i2c_v0_calc_timings,
|
||||||
{ .grf_offset = -1 }, /* no I2C switching needed */
|
};
|
||||||
|
|
||||||
|
static const struct rk3x_i2c_soc_data rk3188_soc_data = {
|
||||||
|
.grf_offset = 0x0a4,
|
||||||
|
.calc_timings = rk3x_i2c_v0_calc_timings,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct rk3x_i2c_soc_data rk3228_soc_data = {
|
||||||
|
.grf_offset = -1,
|
||||||
|
.calc_timings = rk3x_i2c_v0_calc_timings,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct rk3x_i2c_soc_data rk3288_soc_data = {
|
||||||
|
.grf_offset = -1,
|
||||||
|
.calc_timings = rk3x_i2c_v0_calc_timings,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct rk3x_i2c_soc_data rk3399_soc_data = {
|
||||||
|
.grf_offset = -1,
|
||||||
|
.calc_timings = rk3x_i2c_v1_calc_timings,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id rk3x_i2c_match[] = {
|
static const struct of_device_id rk3x_i2c_match[] = {
|
||||||
{ .compatible = "rockchip,rk3066-i2c", .data = (void *)&soc_data[0] },
|
{
|
||||||
{ .compatible = "rockchip,rk3188-i2c", .data = (void *)&soc_data[1] },
|
.compatible = "rockchip,rk3066-i2c",
|
||||||
{ .compatible = "rockchip,rk3228-i2c", .data = (void *)&soc_data[2] },
|
.data = (void *)&rk3066_soc_data
|
||||||
{ .compatible = "rockchip,rk3288-i2c", .data = (void *)&soc_data[2] },
|
},
|
||||||
|
{
|
||||||
|
.compatible = "rockchip,rk3188-i2c",
|
||||||
|
.data = (void *)&rk3188_soc_data
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "rockchip,rk3228-i2c",
|
||||||
|
.data = (void *)&rk3228_soc_data
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "rockchip,rk3288-i2c",
|
||||||
|
.data = (void *)&rk3288_soc_data
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "rockchip,rk3399-i2c",
|
||||||
|
.data = (void *)&rk3399_soc_data
|
||||||
|
},
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, rk3x_i2c_match);
|
MODULE_DEVICE_TABLE(of, rk3x_i2c_match);
|
||||||
|
@ -886,12 +1206,6 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
|
||||||
spin_lock_init(&i2c->lock);
|
spin_lock_init(&i2c->lock);
|
||||||
init_waitqueue_head(&i2c->wait);
|
init_waitqueue_head(&i2c->wait);
|
||||||
|
|
||||||
i2c->clk = devm_clk_get(&pdev->dev, NULL);
|
|
||||||
if (IS_ERR(i2c->clk)) {
|
|
||||||
dev_err(&pdev->dev, "cannot get clock\n");
|
|
||||||
return PTR_ERR(i2c->clk);
|
|
||||||
}
|
|
||||||
|
|
||||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
i2c->regs = devm_ioremap_resource(&pdev->dev, mem);
|
i2c->regs = devm_ioremap_resource(&pdev->dev, mem);
|
||||||
if (IS_ERR(i2c->regs))
|
if (IS_ERR(i2c->regs))
|
||||||
|
@ -945,17 +1259,44 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
platform_set_drvdata(pdev, i2c);
|
platform_set_drvdata(pdev, i2c);
|
||||||
|
|
||||||
|
if (i2c->soc_data->calc_timings == rk3x_i2c_v0_calc_timings) {
|
||||||
|
/* Only one clock to use for bus clock and peripheral clock */
|
||||||
|
i2c->clk = devm_clk_get(&pdev->dev, NULL);
|
||||||
|
i2c->pclk = i2c->clk;
|
||||||
|
} else {
|
||||||
|
i2c->clk = devm_clk_get(&pdev->dev, "i2c");
|
||||||
|
i2c->pclk = devm_clk_get(&pdev->dev, "pclk");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IS_ERR(i2c->clk)) {
|
||||||
|
ret = PTR_ERR(i2c->clk);
|
||||||
|
if (ret != -EPROBE_DEFER)
|
||||||
|
dev_err(&pdev->dev, "Can't get bus clk: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (IS_ERR(i2c->pclk)) {
|
||||||
|
ret = PTR_ERR(i2c->pclk);
|
||||||
|
if (ret != -EPROBE_DEFER)
|
||||||
|
dev_err(&pdev->dev, "Can't get periph clk: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
ret = clk_prepare(i2c->clk);
|
ret = clk_prepare(i2c->clk);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&pdev->dev, "Could not prepare clock\n");
|
dev_err(&pdev->dev, "Can't prepare bus clk: %d\n", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
ret = clk_prepare(i2c->pclk);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&pdev->dev, "Can't prepare periph clock: %d\n", ret);
|
||||||
|
goto err_clk;
|
||||||
|
}
|
||||||
|
|
||||||
i2c->clk_rate_nb.notifier_call = rk3x_i2c_clk_notifier_cb;
|
i2c->clk_rate_nb.notifier_call = rk3x_i2c_clk_notifier_cb;
|
||||||
ret = clk_notifier_register(i2c->clk, &i2c->clk_rate_nb);
|
ret = clk_notifier_register(i2c->clk, &i2c->clk_rate_nb);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
dev_err(&pdev->dev, "Unable to register clock notifier\n");
|
dev_err(&pdev->dev, "Unable to register clock notifier\n");
|
||||||
goto err_clk;
|
goto err_pclk;
|
||||||
}
|
}
|
||||||
|
|
||||||
clk_rate = clk_get_rate(i2c->clk);
|
clk_rate = clk_get_rate(i2c->clk);
|
||||||
|
@ -973,6 +1314,8 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
err_clk_notifier:
|
err_clk_notifier:
|
||||||
clk_notifier_unregister(i2c->clk, &i2c->clk_rate_nb);
|
clk_notifier_unregister(i2c->clk, &i2c->clk_rate_nb);
|
||||||
|
err_pclk:
|
||||||
|
clk_unprepare(i2c->pclk);
|
||||||
err_clk:
|
err_clk:
|
||||||
clk_unprepare(i2c->clk);
|
clk_unprepare(i2c->clk);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -985,6 +1328,7 @@ static int rk3x_i2c_remove(struct platform_device *pdev)
|
||||||
i2c_del_adapter(&i2c->adap);
|
i2c_del_adapter(&i2c->adap);
|
||||||
|
|
||||||
clk_notifier_unregister(i2c->clk, &i2c->clk_rate_nb);
|
clk_notifier_unregister(i2c->clk, &i2c->clk_rate_nb);
|
||||||
|
clk_unprepare(i2c->pclk);
|
||||||
clk_unprepare(i2c->clk);
|
clk_unprepare(i2c->clk);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -125,7 +125,7 @@ static struct i2c_algorithm osif_algorithm = {
|
||||||
#define USB_OSIF_VENDOR_ID 0x1964
|
#define USB_OSIF_VENDOR_ID 0x1964
|
||||||
#define USB_OSIF_PRODUCT_ID 0x0001
|
#define USB_OSIF_PRODUCT_ID 0x0001
|
||||||
|
|
||||||
static struct usb_device_id osif_table[] = {
|
static const struct usb_device_id osif_table[] = {
|
||||||
{ USB_DEVICE(USB_OSIF_VENDOR_ID, USB_OSIF_PRODUCT_ID) },
|
{ USB_DEVICE(USB_OSIF_VENDOR_ID, USB_OSIF_PRODUCT_ID) },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
|
@ -70,28 +70,14 @@ static int i2c_versatile_probe(struct platform_device *dev)
|
||||||
struct resource *r;
|
struct resource *r;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
i2c = devm_kzalloc(&dev->dev, sizeof(struct i2c_versatile), GFP_KERNEL);
|
||||||
|
if (!i2c)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
r = platform_get_resource(dev, IORESOURCE_MEM, 0);
|
r = platform_get_resource(dev, IORESOURCE_MEM, 0);
|
||||||
if (!r) {
|
i2c->base = devm_ioremap_resource(&dev->dev, r);
|
||||||
ret = -EINVAL;
|
if (IS_ERR(i2c->base))
|
||||||
goto err_out;
|
return PTR_ERR(i2c->base);
|
||||||
}
|
|
||||||
|
|
||||||
if (!request_mem_region(r->start, resource_size(r), "versatile-i2c")) {
|
|
||||||
ret = -EBUSY;
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
i2c = kzalloc(sizeof(struct i2c_versatile), GFP_KERNEL);
|
|
||||||
if (!i2c) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto err_release;
|
|
||||||
}
|
|
||||||
|
|
||||||
i2c->base = ioremap(r->start, resource_size(r));
|
|
||||||
if (!i2c->base) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto err_free;
|
|
||||||
}
|
|
||||||
|
|
||||||
writel(SCL | SDA, i2c->base + I2C_CONTROLS);
|
writel(SCL | SDA, i2c->base + I2C_CONTROLS);
|
||||||
|
|
||||||
|
@ -105,18 +91,12 @@ static int i2c_versatile_probe(struct platform_device *dev)
|
||||||
|
|
||||||
i2c->adap.nr = dev->id;
|
i2c->adap.nr = dev->id;
|
||||||
ret = i2c_bit_add_numbered_bus(&i2c->adap);
|
ret = i2c_bit_add_numbered_bus(&i2c->adap);
|
||||||
if (ret >= 0) {
|
if (ret < 0)
|
||||||
platform_set_drvdata(dev, i2c);
|
return ret;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
iounmap(i2c->base);
|
platform_set_drvdata(dev, i2c);
|
||||||
err_free:
|
|
||||||
kfree(i2c);
|
return 0;
|
||||||
err_release:
|
|
||||||
release_mem_region(r->start, resource_size(r));
|
|
||||||
err_out:
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int i2c_versatile_remove(struct platform_device *dev)
|
static int i2c_versatile_remove(struct platform_device *dev)
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
* warranty of any kind, whether express or implied.
|
* warranty of any kind, whether express or implied.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/acpi.h>
|
||||||
#include <linux/completion.h>
|
#include <linux/completion.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
@ -341,11 +342,10 @@ static struct i2c_algorithm xlp9xx_i2c_algo = {
|
||||||
static int xlp9xx_i2c_get_frequency(struct platform_device *pdev,
|
static int xlp9xx_i2c_get_frequency(struct platform_device *pdev,
|
||||||
struct xlp9xx_i2c_dev *priv)
|
struct xlp9xx_i2c_dev *priv)
|
||||||
{
|
{
|
||||||
struct device_node *np = pdev->dev.of_node;
|
|
||||||
u32 freq;
|
u32 freq;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = of_property_read_u32(np, "clock-frequency", &freq);
|
err = device_property_read_u32(&pdev->dev, "clock-frequency", &freq);
|
||||||
if (err) {
|
if (err) {
|
||||||
freq = XLP9XX_I2C_DEFAULT_FREQ;
|
freq = XLP9XX_I2C_DEFAULT_FREQ;
|
||||||
dev_dbg(&pdev->dev, "using default frequency %u\n", freq);
|
dev_dbg(&pdev->dev, "using default frequency %u\n", freq);
|
||||||
|
@ -429,12 +429,21 @@ static const struct of_device_id xlp9xx_i2c_of_match[] = {
|
||||||
{ /* sentinel */ },
|
{ /* sentinel */ },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_ACPI
|
||||||
|
static const struct acpi_device_id xlp9xx_i2c_acpi_ids[] = {
|
||||||
|
{"BRCM9007", 0},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(acpi, xlp9xx_i2c_acpi_ids);
|
||||||
|
#endif
|
||||||
|
|
||||||
static struct platform_driver xlp9xx_i2c_driver = {
|
static struct platform_driver xlp9xx_i2c_driver = {
|
||||||
.probe = xlp9xx_i2c_probe,
|
.probe = xlp9xx_i2c_probe,
|
||||||
.remove = xlp9xx_i2c_remove,
|
.remove = xlp9xx_i2c_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "xlp9xx-i2c",
|
.name = "xlp9xx-i2c",
|
||||||
.of_match_table = xlp9xx_i2c_of_match,
|
.of_match_table = xlp9xx_i2c_of_match,
|
||||||
|
.acpi_match_table = ACPI_PTR(xlp9xx_i2c_acpi_ids),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,8 @@
|
||||||
I2C slave support (c) 2014 by Wolfram Sang <wsa@sang-engineering.com>
|
I2C slave support (c) 2014 by Wolfram Sang <wsa@sang-engineering.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) "i2c-core: " fmt
|
||||||
|
|
||||||
#include <dt-bindings/i2c/i2c.h>
|
#include <dt-bindings/i2c/i2c.h>
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
|
@ -493,7 +495,8 @@ acpi_i2c_space_handler(u32 function, acpi_physical_address command,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
pr_info("protocol(0x%02x) is not supported.\n", accessor_type);
|
dev_warn(&adapter->dev, "protocol 0x%02x not supported for client 0x%02x\n",
|
||||||
|
accessor_type, client->addr);
|
||||||
ret = AE_BAD_PARAMETER;
|
ret = AE_BAD_PARAMETER;
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
@ -759,6 +762,47 @@ int i2c_recover_bus(struct i2c_adapter *adap)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(i2c_recover_bus);
|
EXPORT_SYMBOL_GPL(i2c_recover_bus);
|
||||||
|
|
||||||
|
static void i2c_init_recovery(struct i2c_adapter *adap)
|
||||||
|
{
|
||||||
|
struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
|
||||||
|
char *err_str;
|
||||||
|
|
||||||
|
if (!bri)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!bri->recover_bus) {
|
||||||
|
err_str = "no recover_bus() found";
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generic GPIO recovery */
|
||||||
|
if (bri->recover_bus == i2c_generic_gpio_recovery) {
|
||||||
|
if (!gpio_is_valid(bri->scl_gpio)) {
|
||||||
|
err_str = "invalid SCL gpio";
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gpio_is_valid(bri->sda_gpio))
|
||||||
|
bri->get_sda = get_sda_gpio_value;
|
||||||
|
else
|
||||||
|
bri->get_sda = NULL;
|
||||||
|
|
||||||
|
bri->get_scl = get_scl_gpio_value;
|
||||||
|
bri->set_scl = set_scl_gpio_value;
|
||||||
|
} else if (bri->recover_bus == i2c_generic_scl_recovery) {
|
||||||
|
/* Generic SCL recovery */
|
||||||
|
if (!bri->set_scl || !bri->get_scl) {
|
||||||
|
err_str = "no {get|set}_scl() found";
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
err:
|
||||||
|
dev_err(&adap->dev, "Not using recovery: %s\n", err_str);
|
||||||
|
adap->bus_recovery_info = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static int i2c_device_probe(struct device *dev)
|
static int i2c_device_probe(struct device *dev)
|
||||||
{
|
{
|
||||||
struct i2c_client *client = i2c_verify_client(dev);
|
struct i2c_client *client = i2c_verify_client(dev);
|
||||||
|
@ -1240,6 +1284,47 @@ struct i2c_client *i2c_new_dummy(struct i2c_adapter *adapter, u16 address)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(i2c_new_dummy);
|
EXPORT_SYMBOL_GPL(i2c_new_dummy);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* i2c_new_secondary_device - Helper to get the instantiated secondary address
|
||||||
|
* and create the associated device
|
||||||
|
* @client: Handle to the primary client
|
||||||
|
* @name: Handle to specify which secondary address to get
|
||||||
|
* @default_addr: Used as a fallback if no secondary address was specified
|
||||||
|
* Context: can sleep
|
||||||
|
*
|
||||||
|
* I2C clients can be composed of multiple I2C slaves bound together in a single
|
||||||
|
* component. The I2C client driver then binds to the master I2C slave and needs
|
||||||
|
* to create I2C dummy clients to communicate with all the other slaves.
|
||||||
|
*
|
||||||
|
* This function creates and returns an I2C dummy client whose I2C address is
|
||||||
|
* retrieved from the platform firmware based on the given slave name. If no
|
||||||
|
* address is specified by the firmware default_addr is used.
|
||||||
|
*
|
||||||
|
* On DT-based platforms the address is retrieved from the "reg" property entry
|
||||||
|
* cell whose "reg-names" value matches the slave name.
|
||||||
|
*
|
||||||
|
* This returns the new i2c client, which should be saved for later use with
|
||||||
|
* i2c_unregister_device(); or NULL to indicate an error.
|
||||||
|
*/
|
||||||
|
struct i2c_client *i2c_new_secondary_device(struct i2c_client *client,
|
||||||
|
const char *name,
|
||||||
|
u16 default_addr)
|
||||||
|
{
|
||||||
|
struct device_node *np = client->dev.of_node;
|
||||||
|
u32 addr = default_addr;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (np) {
|
||||||
|
i = of_property_match_string(np, "reg-names", name);
|
||||||
|
if (i >= 0)
|
||||||
|
of_property_read_u32_index(np, "reg", i, &addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_dbg(&client->adapter->dev, "Address for %s : 0x%x\n", name, addr);
|
||||||
|
return i2c_new_dummy(client->adapter, addr);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(i2c_new_secondary_device);
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
/* I2C bus adapters -- one roots each I2C or SMBUS segment */
|
/* I2C bus adapters -- one roots each I2C or SMBUS segment */
|
||||||
|
@ -1608,7 +1693,7 @@ static int __process_new_adapter(struct device_driver *d, void *data)
|
||||||
|
|
||||||
static int i2c_register_adapter(struct i2c_adapter *adap)
|
static int i2c_register_adapter(struct i2c_adapter *adap)
|
||||||
{
|
{
|
||||||
int res = 0;
|
int res = -EINVAL;
|
||||||
|
|
||||||
/* Can't register until after driver model init */
|
/* Can't register until after driver model init */
|
||||||
if (WARN_ON(!is_registered)) {
|
if (WARN_ON(!is_registered)) {
|
||||||
|
@ -1617,15 +1702,12 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sanity checks */
|
/* Sanity checks */
|
||||||
if (unlikely(adap->name[0] == '\0')) {
|
if (WARN(!adap->name[0], "i2c adapter has no name"))
|
||||||
pr_err("i2c-core: Attempt to register an adapter with "
|
goto out_list;
|
||||||
"no name!\n");
|
|
||||||
return -EINVAL;
|
if (!adap->algo) {
|
||||||
}
|
pr_err("adapter '%s': no algo supplied!\n", adap->name);
|
||||||
if (unlikely(!adap->algo)) {
|
goto out_list;
|
||||||
pr_err("i2c-core: Attempt to register adapter '%s' with "
|
|
||||||
"no algo!\n", adap->name);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!adap->lock_bus) {
|
if (!adap->lock_bus) {
|
||||||
|
@ -1647,8 +1729,10 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
|
||||||
adap->dev.bus = &i2c_bus_type;
|
adap->dev.bus = &i2c_bus_type;
|
||||||
adap->dev.type = &i2c_adapter_type;
|
adap->dev.type = &i2c_adapter_type;
|
||||||
res = device_register(&adap->dev);
|
res = device_register(&adap->dev);
|
||||||
if (res)
|
if (res) {
|
||||||
|
pr_err("adapter '%s': can't register device (%d)\n", adap->name, res);
|
||||||
goto out_list;
|
goto out_list;
|
||||||
|
}
|
||||||
|
|
||||||
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
|
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
|
||||||
|
|
||||||
|
@ -1664,41 +1748,8 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
|
||||||
"Failed to create compatibility class link\n");
|
"Failed to create compatibility class link\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* bus recovery specific initialization */
|
i2c_init_recovery(adap);
|
||||||
if (adap->bus_recovery_info) {
|
|
||||||
struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
|
|
||||||
|
|
||||||
if (!bri->recover_bus) {
|
|
||||||
dev_err(&adap->dev, "No recover_bus() found, not using recovery\n");
|
|
||||||
adap->bus_recovery_info = NULL;
|
|
||||||
goto exit_recovery;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Generic GPIO recovery */
|
|
||||||
if (bri->recover_bus == i2c_generic_gpio_recovery) {
|
|
||||||
if (!gpio_is_valid(bri->scl_gpio)) {
|
|
||||||
dev_err(&adap->dev, "Invalid SCL gpio, not using recovery\n");
|
|
||||||
adap->bus_recovery_info = NULL;
|
|
||||||
goto exit_recovery;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gpio_is_valid(bri->sda_gpio))
|
|
||||||
bri->get_sda = get_sda_gpio_value;
|
|
||||||
else
|
|
||||||
bri->get_sda = NULL;
|
|
||||||
|
|
||||||
bri->get_scl = get_scl_gpio_value;
|
|
||||||
bri->set_scl = set_scl_gpio_value;
|
|
||||||
} else if (bri->recover_bus == i2c_generic_scl_recovery) {
|
|
||||||
/* Generic SCL recovery */
|
|
||||||
if (!bri->set_scl || !bri->get_scl) {
|
|
||||||
dev_err(&adap->dev, "No {get|set}_scl() found, not using recovery\n");
|
|
||||||
adap->bus_recovery_info = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exit_recovery:
|
|
||||||
/* create pre-declared device nodes */
|
/* create pre-declared device nodes */
|
||||||
of_i2c_register_devices(adap);
|
of_i2c_register_devices(adap);
|
||||||
acpi_i2c_register_devices(adap);
|
acpi_i2c_register_devices(adap);
|
||||||
|
@ -1730,13 +1781,12 @@ out_list:
|
||||||
*/
|
*/
|
||||||
static int __i2c_add_numbered_adapter(struct i2c_adapter *adap)
|
static int __i2c_add_numbered_adapter(struct i2c_adapter *adap)
|
||||||
{
|
{
|
||||||
int id;
|
int id;
|
||||||
|
|
||||||
mutex_lock(&core_lock);
|
mutex_lock(&core_lock);
|
||||||
id = idr_alloc(&i2c_adapter_idr, adap, adap->nr, adap->nr + 1,
|
id = idr_alloc(&i2c_adapter_idr, adap, adap->nr, adap->nr + 1, GFP_KERNEL);
|
||||||
GFP_KERNEL);
|
|
||||||
mutex_unlock(&core_lock);
|
mutex_unlock(&core_lock);
|
||||||
if (id < 0)
|
if (WARN(id < 0, "couldn't get idr"))
|
||||||
return id == -ENOSPC ? -EBUSY : id;
|
return id == -ENOSPC ? -EBUSY : id;
|
||||||
|
|
||||||
return i2c_register_adapter(adap);
|
return i2c_register_adapter(adap);
|
||||||
|
@ -1773,7 +1823,7 @@ int i2c_add_adapter(struct i2c_adapter *adapter)
|
||||||
id = idr_alloc(&i2c_adapter_idr, adapter,
|
id = idr_alloc(&i2c_adapter_idr, adapter,
|
||||||
__i2c_first_dynamic_bus_num, 0, GFP_KERNEL);
|
__i2c_first_dynamic_bus_num, 0, GFP_KERNEL);
|
||||||
mutex_unlock(&core_lock);
|
mutex_unlock(&core_lock);
|
||||||
if (id < 0)
|
if (WARN(id < 0, "couldn't get idr"))
|
||||||
return id;
|
return id;
|
||||||
|
|
||||||
adapter->nr = id;
|
adapter->nr = id;
|
||||||
|
@ -1871,8 +1921,7 @@ void i2c_del_adapter(struct i2c_adapter *adap)
|
||||||
found = idr_find(&i2c_adapter_idr, adap->nr);
|
found = idr_find(&i2c_adapter_idr, adap->nr);
|
||||||
mutex_unlock(&core_lock);
|
mutex_unlock(&core_lock);
|
||||||
if (found != adap) {
|
if (found != adap) {
|
||||||
pr_debug("i2c-core: attempting to delete unregistered "
|
pr_debug("attempting to delete unregistered adapter [%s]\n", adap->name);
|
||||||
"adapter [%s]\n", adap->name);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2032,7 +2081,7 @@ int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
|
||||||
if (res)
|
if (res)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
|
pr_debug("driver [%s] registered\n", driver->driver.name);
|
||||||
|
|
||||||
INIT_LIST_HEAD(&driver->clients);
|
INIT_LIST_HEAD(&driver->clients);
|
||||||
/* Walk the adapters that are already present */
|
/* Walk the adapters that are already present */
|
||||||
|
@ -2059,7 +2108,7 @@ void i2c_del_driver(struct i2c_driver *driver)
|
||||||
i2c_for_each_dev(driver, __process_removed_driver);
|
i2c_for_each_dev(driver, __process_removed_driver);
|
||||||
|
|
||||||
driver_unregister(&driver->driver);
|
driver_unregister(&driver->driver);
|
||||||
pr_debug("i2c-core: driver [%s] unregistered\n", driver->driver.name);
|
pr_debug("driver [%s] unregistered\n", driver->driver.name);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(i2c_del_driver);
|
EXPORT_SYMBOL(i2c_del_driver);
|
||||||
|
|
||||||
|
@ -2150,8 +2199,8 @@ static int of_i2c_notify(struct notifier_block *nb, unsigned long action,
|
||||||
put_device(&adap->dev);
|
put_device(&adap->dev);
|
||||||
|
|
||||||
if (IS_ERR(client)) {
|
if (IS_ERR(client)) {
|
||||||
pr_err("%s: failed to create for '%s'\n",
|
dev_err(&adap->dev, "failed to create client for '%s'\n",
|
||||||
__func__, rd->dn->full_name);
|
rd->dn->full_name);
|
||||||
return notifier_from_errno(PTR_ERR(client));
|
return notifier_from_errno(PTR_ERR(client));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -2772,7 +2821,7 @@ static int i2c_smbus_check_pec(u8 cpec, struct i2c_msg *msg)
|
||||||
cpec = i2c_smbus_msg_pec(cpec, msg);
|
cpec = i2c_smbus_msg_pec(cpec, msg);
|
||||||
|
|
||||||
if (rpec != cpec) {
|
if (rpec != cpec) {
|
||||||
pr_debug("i2c-core: Bad PEC 0x%02x vs. 0x%02x\n",
|
pr_debug("Bad PEC 0x%02x vs. 0x%02x\n",
|
||||||
rpec, cpec);
|
rpec, cpec);
|
||||||
return -EBADMSG;
|
return -EBADMSG;
|
||||||
}
|
}
|
||||||
|
|
|
@ -485,13 +485,8 @@ static int i2cdev_open(struct inode *inode, struct file *file)
|
||||||
unsigned int minor = iminor(inode);
|
unsigned int minor = iminor(inode);
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
struct i2c_adapter *adap;
|
struct i2c_adapter *adap;
|
||||||
struct i2c_dev *i2c_dev;
|
|
||||||
|
|
||||||
i2c_dev = i2c_dev_get_by_minor(minor);
|
adap = i2c_get_adapter(minor);
|
||||||
if (!i2c_dev)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
adap = i2c_get_adapter(i2c_dev->adap->nr);
|
|
||||||
if (!adap)
|
if (!adap)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,8 @@ struct i2c_smbus_alert {
|
||||||
|
|
||||||
struct alert_data {
|
struct alert_data {
|
||||||
unsigned short addr;
|
unsigned short addr;
|
||||||
u8 flag:1;
|
enum i2c_alert_protocol type;
|
||||||
|
unsigned int data;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* If this is the alerting device, notify its driver */
|
/* If this is the alerting device, notify its driver */
|
||||||
|
@ -56,7 +57,7 @@ static int smbus_do_alert(struct device *dev, void *addrp)
|
||||||
if (client->dev.driver) {
|
if (client->dev.driver) {
|
||||||
driver = to_i2c_driver(client->dev.driver);
|
driver = to_i2c_driver(client->dev.driver);
|
||||||
if (driver->alert)
|
if (driver->alert)
|
||||||
driver->alert(client, data->flag);
|
driver->alert(client, data->type, data->data);
|
||||||
else
|
else
|
||||||
dev_warn(&client->dev, "no driver alert()!\n");
|
dev_warn(&client->dev, "no driver alert()!\n");
|
||||||
} else
|
} else
|
||||||
|
@ -96,8 +97,9 @@ static void smbus_alert(struct work_struct *work)
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
data.flag = status & 1;
|
data.data = status & 1;
|
||||||
data.addr = status >> 1;
|
data.addr = status >> 1;
|
||||||
|
data.type = I2C_PROTOCOL_SMBUS_ALERT;
|
||||||
|
|
||||||
if (data.addr == prev_addr) {
|
if (data.addr == prev_addr) {
|
||||||
dev_warn(&ara->dev, "Duplicate SMBALERT# from dev "
|
dev_warn(&ara->dev, "Duplicate SMBALERT# from dev "
|
||||||
|
@ -105,7 +107,7 @@ static void smbus_alert(struct work_struct *work)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
dev_dbg(&ara->dev, "SMBALERT# from dev 0x%02x, flag %d\n",
|
dev_dbg(&ara->dev, "SMBALERT# from dev 0x%02x, flag %d\n",
|
||||||
data.addr, data.flag);
|
data.addr, data.data);
|
||||||
|
|
||||||
/* Notify driver for the device which issued the alert */
|
/* Notify driver for the device which issued the alert */
|
||||||
device_for_each_child(&ara->adapter->dev, &data,
|
device_for_each_child(&ara->adapter->dev, &data,
|
||||||
|
@ -239,6 +241,108 @@ int i2c_handle_smbus_alert(struct i2c_client *ara)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(i2c_handle_smbus_alert);
|
EXPORT_SYMBOL_GPL(i2c_handle_smbus_alert);
|
||||||
|
|
||||||
|
static void smbus_host_notify_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct alert_data alert;
|
||||||
|
struct i2c_adapter *adapter;
|
||||||
|
unsigned long flags;
|
||||||
|
u16 payload;
|
||||||
|
u8 addr;
|
||||||
|
struct smbus_host_notify *data;
|
||||||
|
|
||||||
|
data = container_of(work, struct smbus_host_notify, work);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&data->lock, flags);
|
||||||
|
payload = data->payload;
|
||||||
|
addr = data->addr;
|
||||||
|
adapter = data->adapter;
|
||||||
|
|
||||||
|
/* clear the pending bit and release the spinlock */
|
||||||
|
data->pending = false;
|
||||||
|
spin_unlock_irqrestore(&data->lock, flags);
|
||||||
|
|
||||||
|
if (!adapter || !addr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
alert.type = I2C_PROTOCOL_SMBUS_HOST_NOTIFY;
|
||||||
|
alert.addr = addr;
|
||||||
|
alert.data = payload;
|
||||||
|
|
||||||
|
device_for_each_child(&adapter->dev, &alert, smbus_do_alert);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* i2c_setup_smbus_host_notify - Allocate a new smbus_host_notify for the given
|
||||||
|
* I2C adapter.
|
||||||
|
* @adapter: the adapter we want to associate a Host Notify function
|
||||||
|
*
|
||||||
|
* Returns a struct smbus_host_notify pointer on success, and NULL on failure.
|
||||||
|
* The resulting smbus_host_notify must not be freed afterwards, it is a
|
||||||
|
* managed resource already.
|
||||||
|
*/
|
||||||
|
struct smbus_host_notify *i2c_setup_smbus_host_notify(struct i2c_adapter *adap)
|
||||||
|
{
|
||||||
|
struct smbus_host_notify *host_notify;
|
||||||
|
|
||||||
|
host_notify = devm_kzalloc(&adap->dev, sizeof(struct smbus_host_notify),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!host_notify)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
host_notify->adapter = adap;
|
||||||
|
|
||||||
|
spin_lock_init(&host_notify->lock);
|
||||||
|
INIT_WORK(&host_notify->work, smbus_host_notify_work);
|
||||||
|
|
||||||
|
return host_notify;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(i2c_setup_smbus_host_notify);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* i2c_handle_smbus_host_notify - Forward a Host Notify event to the correct
|
||||||
|
* I2C client.
|
||||||
|
* @host_notify: the struct host_notify attached to the relevant adapter
|
||||||
|
* @addr: the I2C address of the notifying device
|
||||||
|
* @data: the payload of the notification
|
||||||
|
* Context: can't sleep
|
||||||
|
*
|
||||||
|
* Helper function to be called from an I2C bus driver's interrupt
|
||||||
|
* handler. It will schedule the Host Notify work, in turn calling the
|
||||||
|
* corresponding I2C device driver's alert function.
|
||||||
|
*
|
||||||
|
* host_notify should be a valid pointer previously returned by
|
||||||
|
* i2c_setup_smbus_host_notify().
|
||||||
|
*/
|
||||||
|
int i2c_handle_smbus_host_notify(struct smbus_host_notify *host_notify,
|
||||||
|
unsigned short addr, unsigned int data)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
struct i2c_adapter *adapter;
|
||||||
|
|
||||||
|
if (!host_notify || !host_notify->adapter)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
adapter = host_notify->adapter;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&host_notify->lock, flags);
|
||||||
|
|
||||||
|
if (host_notify->pending) {
|
||||||
|
spin_unlock_irqrestore(&host_notify->lock, flags);
|
||||||
|
dev_warn(&adapter->dev, "Host Notify already scheduled.\n");
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
host_notify->payload = data;
|
||||||
|
host_notify->addr = addr;
|
||||||
|
|
||||||
|
/* Mark that there is a pending notification and release the lock */
|
||||||
|
host_notify->pending = true;
|
||||||
|
spin_unlock_irqrestore(&host_notify->lock, flags);
|
||||||
|
|
||||||
|
return schedule_work(&host_notify->work);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(i2c_handle_smbus_host_notify);
|
||||||
|
|
||||||
module_i2c_driver(smbalert_driver);
|
module_i2c_driver(smbalert_driver);
|
||||||
|
|
||||||
MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
|
MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
|
||||||
|
|
|
@ -58,6 +58,10 @@ struct at24_data {
|
||||||
int use_smbus;
|
int use_smbus;
|
||||||
int use_smbus_write;
|
int use_smbus_write;
|
||||||
|
|
||||||
|
ssize_t (*read_func)(struct at24_data *, char *, unsigned int, size_t);
|
||||||
|
ssize_t (*write_func)(struct at24_data *,
|
||||||
|
const char *, unsigned int, size_t);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Lock protects against activities from other Linux tasks,
|
* Lock protects against activities from other Linux tasks,
|
||||||
* but not from changes by other I2C masters.
|
* but not from changes by other I2C masters.
|
||||||
|
@ -109,25 +113,63 @@ MODULE_PARM_DESC(write_timeout, "Time (in ms) to try writes (default 25)");
|
||||||
((1 << AT24_SIZE_FLAGS | (_flags)) \
|
((1 << AT24_SIZE_FLAGS | (_flags)) \
|
||||||
<< AT24_SIZE_BYTELEN | ilog2(_len))
|
<< AT24_SIZE_BYTELEN | ilog2(_len))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Both reads and writes fail if the previous write didn't complete yet. This
|
||||||
|
* macro loops a few times waiting at least long enough for one entire page
|
||||||
|
* write to work while making sure that at least one iteration is run before
|
||||||
|
* checking the break condition.
|
||||||
|
*
|
||||||
|
* It takes two parameters: a variable in which the future timeout in jiffies
|
||||||
|
* will be stored and a temporary variable holding the time of the last
|
||||||
|
* iteration of processing the request. Both should be unsigned integers
|
||||||
|
* holding at least 32 bits.
|
||||||
|
*/
|
||||||
|
#define loop_until_timeout(tout, op_time) \
|
||||||
|
for (tout = jiffies + msecs_to_jiffies(write_timeout), op_time = 0; \
|
||||||
|
op_time ? time_before(op_time, tout) : true; \
|
||||||
|
usleep_range(1000, 1500), op_time = jiffies)
|
||||||
|
|
||||||
static const struct i2c_device_id at24_ids[] = {
|
static const struct i2c_device_id at24_ids[] = {
|
||||||
/* needs 8 addresses as A0-A2 are ignored */
|
/* needs 8 addresses as A0-A2 are ignored */
|
||||||
{ "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) },
|
{ "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) },
|
||||||
/* old variants can't be handled with this generic entry! */
|
/* old variants can't be handled with this generic entry! */
|
||||||
{ "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) },
|
{ "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) },
|
||||||
{ "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) },
|
{ "24cs01", AT24_DEVICE_MAGIC(16,
|
||||||
|
AT24_FLAG_SERIAL | AT24_FLAG_READONLY) },
|
||||||
|
{ "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) },
|
||||||
|
{ "24cs02", AT24_DEVICE_MAGIC(16,
|
||||||
|
AT24_FLAG_SERIAL | AT24_FLAG_READONLY) },
|
||||||
|
{ "24mac402", AT24_DEVICE_MAGIC(48 / 8,
|
||||||
|
AT24_FLAG_MAC | AT24_FLAG_READONLY) },
|
||||||
|
{ "24mac602", AT24_DEVICE_MAGIC(64 / 8,
|
||||||
|
AT24_FLAG_MAC | AT24_FLAG_READONLY) },
|
||||||
/* spd is a 24c02 in memory DIMMs */
|
/* spd is a 24c02 in memory DIMMs */
|
||||||
{ "spd", AT24_DEVICE_MAGIC(2048 / 8,
|
{ "spd", AT24_DEVICE_MAGIC(2048 / 8,
|
||||||
AT24_FLAG_READONLY | AT24_FLAG_IRUGO) },
|
AT24_FLAG_READONLY | AT24_FLAG_IRUGO) },
|
||||||
{ "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) },
|
{ "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) },
|
||||||
|
{ "24cs04", AT24_DEVICE_MAGIC(16,
|
||||||
|
AT24_FLAG_SERIAL | AT24_FLAG_READONLY) },
|
||||||
/* 24rf08 quirk is handled at i2c-core */
|
/* 24rf08 quirk is handled at i2c-core */
|
||||||
{ "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) },
|
{ "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) },
|
||||||
{ "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) },
|
{ "24cs08", AT24_DEVICE_MAGIC(16,
|
||||||
{ "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) },
|
AT24_FLAG_SERIAL | AT24_FLAG_READONLY) },
|
||||||
{ "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) },
|
{ "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) },
|
||||||
{ "24c128", AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) },
|
{ "24cs16", AT24_DEVICE_MAGIC(16,
|
||||||
{ "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) },
|
AT24_FLAG_SERIAL | AT24_FLAG_READONLY) },
|
||||||
{ "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) },
|
{ "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) },
|
||||||
{ "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) },
|
{ "24cs32", AT24_DEVICE_MAGIC(16,
|
||||||
|
AT24_FLAG_ADDR16 |
|
||||||
|
AT24_FLAG_SERIAL |
|
||||||
|
AT24_FLAG_READONLY) },
|
||||||
|
{ "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) },
|
||||||
|
{ "24cs64", AT24_DEVICE_MAGIC(16,
|
||||||
|
AT24_FLAG_ADDR16 |
|
||||||
|
AT24_FLAG_SERIAL |
|
||||||
|
AT24_FLAG_READONLY) },
|
||||||
|
{ "24c128", AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) },
|
||||||
|
{ "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) },
|
||||||
|
{ "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) },
|
||||||
|
{ "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) },
|
||||||
{ "at24", 0 },
|
{ "at24", 0 },
|
||||||
{ /* END OF LIST */ }
|
{ /* END OF LIST */ }
|
||||||
};
|
};
|
||||||
|
@ -145,9 +187,22 @@ MODULE_DEVICE_TABLE(acpi, at24_acpi_ids);
|
||||||
* This routine supports chips which consume multiple I2C addresses. It
|
* This routine supports chips which consume multiple I2C addresses. It
|
||||||
* computes the addressing information to be used for a given r/w request.
|
* computes the addressing information to be used for a given r/w request.
|
||||||
* Assumes that sanity checks for offset happened at sysfs-layer.
|
* Assumes that sanity checks for offset happened at sysfs-layer.
|
||||||
|
*
|
||||||
|
* Slave address and byte offset derive from the offset. Always
|
||||||
|
* set the byte address; on a multi-master board, another master
|
||||||
|
* may have changed the chip's "current" address pointer.
|
||||||
|
*
|
||||||
|
* REVISIT some multi-address chips don't rollover page reads to
|
||||||
|
* the next slave address, so we may need to truncate the count.
|
||||||
|
* Those chips might need another quirk flag.
|
||||||
|
*
|
||||||
|
* If the real hardware used four adjacent 24c02 chips and that
|
||||||
|
* were misconfigured as one 24c08, that would be a similar effect:
|
||||||
|
* one "eeprom" file not four, but larger reads would fail when
|
||||||
|
* they crossed certain pages.
|
||||||
*/
|
*/
|
||||||
static struct i2c_client *at24_translate_offset(struct at24_data *at24,
|
static struct i2c_client *at24_translate_offset(struct at24_data *at24,
|
||||||
unsigned *offset)
|
unsigned int *offset)
|
||||||
{
|
{
|
||||||
unsigned i;
|
unsigned i;
|
||||||
|
|
||||||
|
@ -162,89 +217,283 @@ static struct i2c_client *at24_translate_offset(struct at24_data *at24,
|
||||||
return at24->client[i];
|
return at24->client[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
|
static ssize_t at24_eeprom_read_smbus(struct at24_data *at24, char *buf,
|
||||||
unsigned offset, size_t count)
|
unsigned int offset, size_t count)
|
||||||
{
|
{
|
||||||
struct i2c_msg msg[2];
|
|
||||||
u8 msgbuf[2];
|
|
||||||
struct i2c_client *client;
|
|
||||||
unsigned long timeout, read_time;
|
unsigned long timeout, read_time;
|
||||||
int status, i;
|
struct i2c_client *client;
|
||||||
|
int status;
|
||||||
|
|
||||||
memset(msg, 0, sizeof(msg));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* REVISIT some multi-address chips don't rollover page reads to
|
|
||||||
* the next slave address, so we may need to truncate the count.
|
|
||||||
* Those chips might need another quirk flag.
|
|
||||||
*
|
|
||||||
* If the real hardware used four adjacent 24c02 chips and that
|
|
||||||
* were misconfigured as one 24c08, that would be a similar effect:
|
|
||||||
* one "eeprom" file not four, but larger reads would fail when
|
|
||||||
* they crossed certain pages.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Slave address and byte offset derive from the offset. Always
|
|
||||||
* set the byte address; on a multi-master board, another master
|
|
||||||
* may have changed the chip's "current" address pointer.
|
|
||||||
*/
|
|
||||||
client = at24_translate_offset(at24, &offset);
|
client = at24_translate_offset(at24, &offset);
|
||||||
|
|
||||||
if (count > io_limit)
|
if (count > io_limit)
|
||||||
count = io_limit;
|
count = io_limit;
|
||||||
|
|
||||||
if (at24->use_smbus) {
|
/* Smaller eeproms can work given some SMBus extension calls */
|
||||||
/* Smaller eeproms can work given some SMBus extension calls */
|
if (count > I2C_SMBUS_BLOCK_MAX)
|
||||||
if (count > I2C_SMBUS_BLOCK_MAX)
|
count = I2C_SMBUS_BLOCK_MAX;
|
||||||
count = I2C_SMBUS_BLOCK_MAX;
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* When we have a better choice than SMBus calls, use a
|
|
||||||
* combined I2C message. Write address; then read up to
|
|
||||||
* io_limit data bytes. Note that read page rollover helps us
|
|
||||||
* here (unlike writes). msgbuf is u8 and will cast to our
|
|
||||||
* needs.
|
|
||||||
*/
|
|
||||||
i = 0;
|
|
||||||
if (at24->chip.flags & AT24_FLAG_ADDR16)
|
|
||||||
msgbuf[i++] = offset >> 8;
|
|
||||||
msgbuf[i++] = offset;
|
|
||||||
|
|
||||||
msg[0].addr = client->addr;
|
loop_until_timeout(timeout, read_time) {
|
||||||
msg[0].buf = msgbuf;
|
status = i2c_smbus_read_i2c_block_data_or_emulated(client,
|
||||||
msg[0].len = i;
|
offset,
|
||||||
|
count, buf);
|
||||||
|
|
||||||
msg[1].addr = client->addr;
|
|
||||||
msg[1].flags = I2C_M_RD;
|
|
||||||
msg[1].buf = buf;
|
|
||||||
msg[1].len = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Reads fail if the previous write didn't complete yet. We may
|
|
||||||
* loop a few times until this one succeeds, waiting at least
|
|
||||||
* long enough for one entire page write to work.
|
|
||||||
*/
|
|
||||||
timeout = jiffies + msecs_to_jiffies(write_timeout);
|
|
||||||
do {
|
|
||||||
read_time = jiffies;
|
|
||||||
if (at24->use_smbus) {
|
|
||||||
status = i2c_smbus_read_i2c_block_data_or_emulated(client, offset,
|
|
||||||
count, buf);
|
|
||||||
} else {
|
|
||||||
status = i2c_transfer(client->adapter, msg, 2);
|
|
||||||
if (status == 2)
|
|
||||||
status = count;
|
|
||||||
}
|
|
||||||
dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n",
|
dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n",
|
||||||
count, offset, status, jiffies);
|
count, offset, status, jiffies);
|
||||||
|
|
||||||
if (status == count)
|
if (status == count)
|
||||||
return count;
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
usleep_range(1000, 1500);
|
return -ETIMEDOUT;
|
||||||
} while (time_before(read_time, timeout));
|
}
|
||||||
|
|
||||||
|
static ssize_t at24_eeprom_read_i2c(struct at24_data *at24, char *buf,
|
||||||
|
unsigned int offset, size_t count)
|
||||||
|
{
|
||||||
|
unsigned long timeout, read_time;
|
||||||
|
struct i2c_client *client;
|
||||||
|
struct i2c_msg msg[2];
|
||||||
|
int status, i;
|
||||||
|
u8 msgbuf[2];
|
||||||
|
|
||||||
|
memset(msg, 0, sizeof(msg));
|
||||||
|
client = at24_translate_offset(at24, &offset);
|
||||||
|
|
||||||
|
if (count > io_limit)
|
||||||
|
count = io_limit;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When we have a better choice than SMBus calls, use a combined I2C
|
||||||
|
* message. Write address; then read up to io_limit data bytes. Note
|
||||||
|
* that read page rollover helps us here (unlike writes). msgbuf is
|
||||||
|
* u8 and will cast to our needs.
|
||||||
|
*/
|
||||||
|
i = 0;
|
||||||
|
if (at24->chip.flags & AT24_FLAG_ADDR16)
|
||||||
|
msgbuf[i++] = offset >> 8;
|
||||||
|
msgbuf[i++] = offset;
|
||||||
|
|
||||||
|
msg[0].addr = client->addr;
|
||||||
|
msg[0].buf = msgbuf;
|
||||||
|
msg[0].len = i;
|
||||||
|
|
||||||
|
msg[1].addr = client->addr;
|
||||||
|
msg[1].flags = I2C_M_RD;
|
||||||
|
msg[1].buf = buf;
|
||||||
|
msg[1].len = count;
|
||||||
|
|
||||||
|
loop_until_timeout(timeout, read_time) {
|
||||||
|
status = i2c_transfer(client->adapter, msg, 2);
|
||||||
|
if (status == 2)
|
||||||
|
status = count;
|
||||||
|
|
||||||
|
dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n",
|
||||||
|
count, offset, status, jiffies);
|
||||||
|
|
||||||
|
if (status == count)
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t at24_eeprom_read_serial(struct at24_data *at24, char *buf,
|
||||||
|
unsigned int offset, size_t count)
|
||||||
|
{
|
||||||
|
unsigned long timeout, read_time;
|
||||||
|
struct i2c_client *client;
|
||||||
|
struct i2c_msg msg[2];
|
||||||
|
u8 addrbuf[2];
|
||||||
|
int status;
|
||||||
|
|
||||||
|
client = at24_translate_offset(at24, &offset);
|
||||||
|
|
||||||
|
memset(msg, 0, sizeof(msg));
|
||||||
|
msg[0].addr = client->addr;
|
||||||
|
msg[0].buf = addrbuf;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The address pointer of the device is shared between the regular
|
||||||
|
* EEPROM array and the serial number block. The dummy write (part of
|
||||||
|
* the sequential read protocol) ensures the address pointer is reset
|
||||||
|
* to the desired position.
|
||||||
|
*/
|
||||||
|
if (at24->chip.flags & AT24_FLAG_ADDR16) {
|
||||||
|
/*
|
||||||
|
* For 16 bit address pointers, the word address must contain
|
||||||
|
* a '10' sequence in bits 11 and 10 regardless of the
|
||||||
|
* intended position of the address pointer.
|
||||||
|
*/
|
||||||
|
addrbuf[0] = 0x08;
|
||||||
|
addrbuf[1] = offset;
|
||||||
|
msg[0].len = 2;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Otherwise the word address must begin with a '10' sequence,
|
||||||
|
* regardless of the intended address.
|
||||||
|
*/
|
||||||
|
addrbuf[0] = 0x80 + offset;
|
||||||
|
msg[0].len = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg[1].addr = client->addr;
|
||||||
|
msg[1].flags = I2C_M_RD;
|
||||||
|
msg[1].buf = buf;
|
||||||
|
msg[1].len = count;
|
||||||
|
|
||||||
|
loop_until_timeout(timeout, read_time) {
|
||||||
|
status = i2c_transfer(client->adapter, msg, 2);
|
||||||
|
if (status == 2)
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t at24_eeprom_read_mac(struct at24_data *at24, char *buf,
|
||||||
|
unsigned int offset, size_t count)
|
||||||
|
{
|
||||||
|
unsigned long timeout, read_time;
|
||||||
|
struct i2c_client *client;
|
||||||
|
struct i2c_msg msg[2];
|
||||||
|
u8 addrbuf[2];
|
||||||
|
int status;
|
||||||
|
|
||||||
|
client = at24_translate_offset(at24, &offset);
|
||||||
|
|
||||||
|
memset(msg, 0, sizeof(msg));
|
||||||
|
msg[0].addr = client->addr;
|
||||||
|
msg[0].buf = addrbuf;
|
||||||
|
addrbuf[0] = 0x90 + offset;
|
||||||
|
msg[0].len = 1;
|
||||||
|
msg[1].addr = client->addr;
|
||||||
|
msg[1].flags = I2C_M_RD;
|
||||||
|
msg[1].buf = buf;
|
||||||
|
msg[1].len = count;
|
||||||
|
|
||||||
|
loop_until_timeout(timeout, read_time) {
|
||||||
|
status = i2c_transfer(client->adapter, msg, 2);
|
||||||
|
if (status == 2)
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note that if the hardware write-protect pin is pulled high, the whole
|
||||||
|
* chip is normally write protected. But there are plenty of product
|
||||||
|
* variants here, including OTP fuses and partial chip protect.
|
||||||
|
*
|
||||||
|
* We only use page mode writes; the alternative is sloooow. These routines
|
||||||
|
* write at most one page.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static size_t at24_adjust_write_count(struct at24_data *at24,
|
||||||
|
unsigned int offset, size_t count)
|
||||||
|
{
|
||||||
|
unsigned next_page;
|
||||||
|
|
||||||
|
/* write_max is at most a page */
|
||||||
|
if (count > at24->write_max)
|
||||||
|
count = at24->write_max;
|
||||||
|
|
||||||
|
/* Never roll over backwards, to the start of this page */
|
||||||
|
next_page = roundup(offset + 1, at24->chip.page_size);
|
||||||
|
if (offset + count > next_page)
|
||||||
|
count = next_page - offset;
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t at24_eeprom_write_smbus_block(struct at24_data *at24,
|
||||||
|
const char *buf,
|
||||||
|
unsigned int offset, size_t count)
|
||||||
|
{
|
||||||
|
unsigned long timeout, write_time;
|
||||||
|
struct i2c_client *client;
|
||||||
|
ssize_t status = 0;
|
||||||
|
|
||||||
|
client = at24_translate_offset(at24, &offset);
|
||||||
|
count = at24_adjust_write_count(at24, offset, count);
|
||||||
|
|
||||||
|
loop_until_timeout(timeout, write_time) {
|
||||||
|
status = i2c_smbus_write_i2c_block_data(client,
|
||||||
|
offset, count, buf);
|
||||||
|
if (status == 0)
|
||||||
|
status = count;
|
||||||
|
|
||||||
|
dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n",
|
||||||
|
count, offset, status, jiffies);
|
||||||
|
|
||||||
|
if (status == count)
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t at24_eeprom_write_smbus_byte(struct at24_data *at24,
|
||||||
|
const char *buf,
|
||||||
|
unsigned int offset, size_t count)
|
||||||
|
{
|
||||||
|
unsigned long timeout, write_time;
|
||||||
|
struct i2c_client *client;
|
||||||
|
ssize_t status = 0;
|
||||||
|
|
||||||
|
client = at24_translate_offset(at24, &offset);
|
||||||
|
|
||||||
|
loop_until_timeout(timeout, write_time) {
|
||||||
|
status = i2c_smbus_write_byte_data(client, offset, buf[0]);
|
||||||
|
if (status == 0)
|
||||||
|
status = count;
|
||||||
|
|
||||||
|
dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n",
|
||||||
|
count, offset, status, jiffies);
|
||||||
|
|
||||||
|
if (status == count)
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t at24_eeprom_write_i2c(struct at24_data *at24, const char *buf,
|
||||||
|
unsigned int offset, size_t count)
|
||||||
|
{
|
||||||
|
unsigned long timeout, write_time;
|
||||||
|
struct i2c_client *client;
|
||||||
|
struct i2c_msg msg;
|
||||||
|
ssize_t status = 0;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
client = at24_translate_offset(at24, &offset);
|
||||||
|
count = at24_adjust_write_count(at24, offset, count);
|
||||||
|
|
||||||
|
msg.addr = client->addr;
|
||||||
|
msg.flags = 0;
|
||||||
|
|
||||||
|
/* msg.buf is u8 and casts will mask the values */
|
||||||
|
msg.buf = at24->writebuf;
|
||||||
|
if (at24->chip.flags & AT24_FLAG_ADDR16)
|
||||||
|
msg.buf[i++] = offset >> 8;
|
||||||
|
|
||||||
|
msg.buf[i++] = offset;
|
||||||
|
memcpy(&msg.buf[i], buf, count);
|
||||||
|
msg.len = i + count;
|
||||||
|
|
||||||
|
loop_until_timeout(timeout, write_time) {
|
||||||
|
status = i2c_transfer(client->adapter, &msg, 1);
|
||||||
|
if (status == 1)
|
||||||
|
status = count;
|
||||||
|
|
||||||
|
dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n",
|
||||||
|
count, offset, status, jiffies);
|
||||||
|
|
||||||
|
if (status == count)
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
@ -266,7 +515,7 @@ static int at24_read(void *priv, unsigned int off, void *val, size_t count)
|
||||||
while (count) {
|
while (count) {
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
status = at24_eeprom_read(at24, buf, off, count);
|
status = at24->read_func(at24, buf, off, count);
|
||||||
if (status < 0) {
|
if (status < 0) {
|
||||||
mutex_unlock(&at24->lock);
|
mutex_unlock(&at24->lock);
|
||||||
return status;
|
return status;
|
||||||
|
@ -281,91 +530,6 @@ static int at24_read(void *priv, unsigned int off, void *val, size_t count)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Note that if the hardware write-protect pin is pulled high, the whole
|
|
||||||
* chip is normally write protected. But there are plenty of product
|
|
||||||
* variants here, including OTP fuses and partial chip protect.
|
|
||||||
*
|
|
||||||
* We only use page mode writes; the alternative is sloooow. This routine
|
|
||||||
* writes at most one page.
|
|
||||||
*/
|
|
||||||
static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf,
|
|
||||||
unsigned offset, size_t count)
|
|
||||||
{
|
|
||||||
struct i2c_client *client;
|
|
||||||
struct i2c_msg msg;
|
|
||||||
ssize_t status = 0;
|
|
||||||
unsigned long timeout, write_time;
|
|
||||||
unsigned next_page;
|
|
||||||
|
|
||||||
/* Get corresponding I2C address and adjust offset */
|
|
||||||
client = at24_translate_offset(at24, &offset);
|
|
||||||
|
|
||||||
/* write_max is at most a page */
|
|
||||||
if (count > at24->write_max)
|
|
||||||
count = at24->write_max;
|
|
||||||
|
|
||||||
/* Never roll over backwards, to the start of this page */
|
|
||||||
next_page = roundup(offset + 1, at24->chip.page_size);
|
|
||||||
if (offset + count > next_page)
|
|
||||||
count = next_page - offset;
|
|
||||||
|
|
||||||
/* If we'll use I2C calls for I/O, set up the message */
|
|
||||||
if (!at24->use_smbus) {
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
msg.addr = client->addr;
|
|
||||||
msg.flags = 0;
|
|
||||||
|
|
||||||
/* msg.buf is u8 and casts will mask the values */
|
|
||||||
msg.buf = at24->writebuf;
|
|
||||||
if (at24->chip.flags & AT24_FLAG_ADDR16)
|
|
||||||
msg.buf[i++] = offset >> 8;
|
|
||||||
|
|
||||||
msg.buf[i++] = offset;
|
|
||||||
memcpy(&msg.buf[i], buf, count);
|
|
||||||
msg.len = i + count;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Writes fail if the previous one didn't complete yet. We may
|
|
||||||
* loop a few times until this one succeeds, waiting at least
|
|
||||||
* long enough for one entire page write to work.
|
|
||||||
*/
|
|
||||||
timeout = jiffies + msecs_to_jiffies(write_timeout);
|
|
||||||
do {
|
|
||||||
write_time = jiffies;
|
|
||||||
if (at24->use_smbus_write) {
|
|
||||||
switch (at24->use_smbus_write) {
|
|
||||||
case I2C_SMBUS_I2C_BLOCK_DATA:
|
|
||||||
status = i2c_smbus_write_i2c_block_data(client,
|
|
||||||
offset, count, buf);
|
|
||||||
break;
|
|
||||||
case I2C_SMBUS_BYTE_DATA:
|
|
||||||
status = i2c_smbus_write_byte_data(client,
|
|
||||||
offset, buf[0]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status == 0)
|
|
||||||
status = count;
|
|
||||||
} else {
|
|
||||||
status = i2c_transfer(client->adapter, &msg, 1);
|
|
||||||
if (status == 1)
|
|
||||||
status = count;
|
|
||||||
}
|
|
||||||
dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n",
|
|
||||||
count, offset, status, jiffies);
|
|
||||||
|
|
||||||
if (status == count)
|
|
||||||
return count;
|
|
||||||
|
|
||||||
usleep_range(1000, 1500);
|
|
||||||
} while (time_before(write_time, timeout));
|
|
||||||
|
|
||||||
return -ETIMEDOUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int at24_write(void *priv, unsigned int off, void *val, size_t count)
|
static int at24_write(void *priv, unsigned int off, void *val, size_t count)
|
||||||
{
|
{
|
||||||
struct at24_data *at24 = priv;
|
struct at24_data *at24 = priv;
|
||||||
|
@ -383,7 +547,7 @@ static int at24_write(void *priv, unsigned int off, void *val, size_t count)
|
||||||
while (count) {
|
while (count) {
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
status = at24_eeprom_write(at24, buf, off, count);
|
status = at24->write_func(at24, buf, off, count);
|
||||||
if (status < 0) {
|
if (status < 0) {
|
||||||
mutex_unlock(&at24->lock);
|
mutex_unlock(&at24->lock);
|
||||||
return status;
|
return status;
|
||||||
|
@ -400,7 +564,7 @@ static int at24_write(void *priv, unsigned int off, void *val, size_t count)
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
#ifdef CONFIG_OF
|
||||||
static void at24_get_ofdata(struct i2c_client *client,
|
static void at24_get_ofdata(struct i2c_client *client,
|
||||||
struct at24_platform_data *chip)
|
struct at24_platform_data *chip)
|
||||||
{
|
{
|
||||||
const __be32 *val;
|
const __be32 *val;
|
||||||
struct device_node *node = client->dev.of_node;
|
struct device_node *node = client->dev.of_node;
|
||||||
|
@ -415,7 +579,7 @@ static void at24_get_ofdata(struct i2c_client *client,
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static void at24_get_ofdata(struct i2c_client *client,
|
static void at24_get_ofdata(struct i2c_client *client,
|
||||||
struct at24_platform_data *chip)
|
struct at24_platform_data *chip)
|
||||||
{ }
|
{ }
|
||||||
#endif /* CONFIG_OF */
|
#endif /* CONFIG_OF */
|
||||||
|
|
||||||
|
@ -518,6 +682,30 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||||
at24->chip = chip;
|
at24->chip = chip;
|
||||||
at24->num_addresses = num_addresses;
|
at24->num_addresses = num_addresses;
|
||||||
|
|
||||||
|
if ((chip.flags & AT24_FLAG_SERIAL) && (chip.flags & AT24_FLAG_MAC)) {
|
||||||
|
dev_err(&client->dev,
|
||||||
|
"invalid device data - cannot have both AT24_FLAG_SERIAL & AT24_FLAG_MAC.");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chip.flags & AT24_FLAG_SERIAL) {
|
||||||
|
at24->read_func = at24_eeprom_read_serial;
|
||||||
|
} else if (chip.flags & AT24_FLAG_MAC) {
|
||||||
|
at24->read_func = at24_eeprom_read_mac;
|
||||||
|
} else {
|
||||||
|
at24->read_func = at24->use_smbus ? at24_eeprom_read_smbus
|
||||||
|
: at24_eeprom_read_i2c;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (at24->use_smbus) {
|
||||||
|
if (at24->use_smbus_write == I2C_SMBUS_I2C_BLOCK_DATA)
|
||||||
|
at24->write_func = at24_eeprom_write_smbus_block;
|
||||||
|
else
|
||||||
|
at24->write_func = at24_eeprom_write_smbus_byte;
|
||||||
|
} else {
|
||||||
|
at24->write_func = at24_eeprom_write_i2c;
|
||||||
|
}
|
||||||
|
|
||||||
writable = !(chip.flags & AT24_FLAG_READONLY);
|
writable = !(chip.flags & AT24_FLAG_READONLY);
|
||||||
if (writable) {
|
if (writable) {
|
||||||
if (!use_smbus || use_smbus_write) {
|
if (!use_smbus || use_smbus_write) {
|
||||||
|
|
|
@ -23,6 +23,8 @@
|
||||||
#define _LINUX_I2C_SMBUS_H
|
#define _LINUX_I2C_SMBUS_H
|
||||||
|
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -48,4 +50,31 @@ struct i2c_client *i2c_setup_smbus_alert(struct i2c_adapter *adapter,
|
||||||
struct i2c_smbus_alert_setup *setup);
|
struct i2c_smbus_alert_setup *setup);
|
||||||
int i2c_handle_smbus_alert(struct i2c_client *ara);
|
int i2c_handle_smbus_alert(struct i2c_client *ara);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* smbus_host_notify - internal structure used by the Host Notify mechanism.
|
||||||
|
* @adapter: the I2C adapter associated with this struct
|
||||||
|
* @work: worker used to schedule the IRQ in the slave device
|
||||||
|
* @lock: spinlock to check if a notification is already pending
|
||||||
|
* @pending: flag set when a notification is pending (any new notification will
|
||||||
|
* be rejected if pending is true)
|
||||||
|
* @payload: the actual payload of the Host Notify event
|
||||||
|
* @addr: the address of the slave device which raised the notification
|
||||||
|
*
|
||||||
|
* This struct needs to be allocated by i2c_setup_smbus_host_notify() and does
|
||||||
|
* not need to be freed. Internally, i2c_setup_smbus_host_notify() uses a
|
||||||
|
* managed resource to clean this up when the adapter get released.
|
||||||
|
*/
|
||||||
|
struct smbus_host_notify {
|
||||||
|
struct i2c_adapter *adapter;
|
||||||
|
struct work_struct work;
|
||||||
|
spinlock_t lock;
|
||||||
|
bool pending;
|
||||||
|
u16 payload;
|
||||||
|
u8 addr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct smbus_host_notify *i2c_setup_smbus_host_notify(struct i2c_adapter *adap);
|
||||||
|
int i2c_handle_smbus_host_notify(struct smbus_host_notify *host_notify,
|
||||||
|
unsigned short addr, unsigned int data);
|
||||||
|
|
||||||
#endif /* _LINUX_I2C_SMBUS_H */
|
#endif /* _LINUX_I2C_SMBUS_H */
|
||||||
|
|
|
@ -126,6 +126,11 @@ i2c_smbus_read_i2c_block_data_or_emulated(const struct i2c_client *client,
|
||||||
u8 command, u8 length, u8 *values);
|
u8 command, u8 length, u8 *values);
|
||||||
#endif /* I2C */
|
#endif /* I2C */
|
||||||
|
|
||||||
|
enum i2c_alert_protocol {
|
||||||
|
I2C_PROTOCOL_SMBUS_ALERT,
|
||||||
|
I2C_PROTOCOL_SMBUS_HOST_NOTIFY,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct i2c_driver - represent an I2C device driver
|
* struct i2c_driver - represent an I2C device driver
|
||||||
* @class: What kind of i2c device we instantiate (for detect)
|
* @class: What kind of i2c device we instantiate (for detect)
|
||||||
|
@ -180,8 +185,11 @@ struct i2c_driver {
|
||||||
* The format and meaning of the data value depends on the protocol.
|
* The format and meaning of the data value depends on the protocol.
|
||||||
* For the SMBus alert protocol, there is a single bit of data passed
|
* For the SMBus alert protocol, there is a single bit of data passed
|
||||||
* as the alert response's low bit ("event flag").
|
* as the alert response's low bit ("event flag").
|
||||||
|
* For the SMBus Host Notify protocol, the data corresponds to the
|
||||||
|
* 16-bit payload data reported by the slave device acting as master.
|
||||||
*/
|
*/
|
||||||
void (*alert)(struct i2c_client *, unsigned int data);
|
void (*alert)(struct i2c_client *, enum i2c_alert_protocol protocol,
|
||||||
|
unsigned int data);
|
||||||
|
|
||||||
/* a ioctl like command that can be used to perform specific functions
|
/* a ioctl like command that can be used to perform specific functions
|
||||||
* with the device.
|
* with the device.
|
||||||
|
@ -349,6 +357,11 @@ extern int i2c_probe_func_quick_read(struct i2c_adapter *, unsigned short addr);
|
||||||
extern struct i2c_client *
|
extern struct i2c_client *
|
||||||
i2c_new_dummy(struct i2c_adapter *adap, u16 address);
|
i2c_new_dummy(struct i2c_adapter *adap, u16 address);
|
||||||
|
|
||||||
|
extern struct i2c_client *
|
||||||
|
i2c_new_secondary_device(struct i2c_client *client,
|
||||||
|
const char *name,
|
||||||
|
u16 default_addr);
|
||||||
|
|
||||||
extern void i2c_unregister_device(struct i2c_client *);
|
extern void i2c_unregister_device(struct i2c_client *);
|
||||||
#endif /* I2C */
|
#endif /* I2C */
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/nvmem-consumer.h>
|
#include <linux/nvmem-consumer.h>
|
||||||
|
#include <linux/bitops.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct at24_platform_data - data to set up at24 (generic eeprom) driver
|
* struct at24_platform_data - data to set up at24 (generic eeprom) driver
|
||||||
|
@ -43,10 +44,12 @@ struct at24_platform_data {
|
||||||
u32 byte_len; /* size (sum of all addr) */
|
u32 byte_len; /* size (sum of all addr) */
|
||||||
u16 page_size; /* for writes */
|
u16 page_size; /* for writes */
|
||||||
u8 flags;
|
u8 flags;
|
||||||
#define AT24_FLAG_ADDR16 0x80 /* address pointer is 16 bit */
|
#define AT24_FLAG_ADDR16 BIT(7) /* address pointer is 16 bit */
|
||||||
#define AT24_FLAG_READONLY 0x40 /* sysfs-entry will be read-only */
|
#define AT24_FLAG_READONLY BIT(6) /* sysfs-entry will be read-only */
|
||||||
#define AT24_FLAG_IRUGO 0x20 /* sysfs-entry will be world-readable */
|
#define AT24_FLAG_IRUGO BIT(5) /* sysfs-entry will be world-readable */
|
||||||
#define AT24_FLAG_TAKE8ADDR 0x10 /* take always 8 addresses (24c00) */
|
#define AT24_FLAG_TAKE8ADDR BIT(4) /* take always 8 addresses (24c00) */
|
||||||
|
#define AT24_FLAG_SERIAL BIT(3) /* factory-programmed serial number */
|
||||||
|
#define AT24_FLAG_MAC BIT(2) /* factory-programmed mac address */
|
||||||
|
|
||||||
void (*setup)(struct nvmem_device *nvmem, void *context);
|
void (*setup)(struct nvmem_device *nvmem, void *context);
|
||||||
void *context;
|
void *context;
|
||||||
|
|
|
@ -102,6 +102,7 @@ struct i2c_msg {
|
||||||
#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000
|
#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000
|
||||||
#define I2C_FUNC_SMBUS_READ_I2C_BLOCK 0x04000000 /* I2C-like block xfer */
|
#define I2C_FUNC_SMBUS_READ_I2C_BLOCK 0x04000000 /* I2C-like block xfer */
|
||||||
#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK 0x08000000 /* w/ 1-byte reg. addr. */
|
#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK 0x08000000 /* w/ 1-byte reg. addr. */
|
||||||
|
#define I2C_FUNC_SMBUS_HOST_NOTIFY 0x10000000
|
||||||
|
|
||||||
#define I2C_FUNC_SMBUS_BYTE (I2C_FUNC_SMBUS_READ_BYTE | \
|
#define I2C_FUNC_SMBUS_BYTE (I2C_FUNC_SMBUS_READ_BYTE | \
|
||||||
I2C_FUNC_SMBUS_WRITE_BYTE)
|
I2C_FUNC_SMBUS_WRITE_BYTE)
|
||||||
|
|
Загрузка…
Ссылка в новой задаче