mirror of https://gitee.com/openkylin/linux.git
Merge branch 'i2c/for-4.17' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang: -I2C core now reports proper OF style module alias. I'd like to repeat the note from the commit msg here (Thanks, Javier!): NOTE: This patch may break out-of-tree drivers that were relying on this behavior, and only had an I2C device ID table even when the device was registered via OF. There are no remaining drivers in mainline that do this, but out-of-tree drivers have to be fixed and define a proper OF device ID table to have module auto-loading working. - new driver for the SynQuacer I2C controller - major refactoring of the QUP driver - the piix4 driver now uses request_muxed_region which should fix a long standing resource conflict with the sp5100_tco watchdog - a bunch of small core & driver improvements * 'i2c/for-4.17' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (53 commits) i2c: add support for Socionext SynQuacer I2C controller dt-bindings: i2c: add binding for Socionext SynQuacer I2C i2c: Update i2c_trace_msg static key to modern api i2c: fix parameter of trace_i2c_result i2c: imx: avoid taking clk_prepare mutex in PM callbacks i2c: imx: use clk notifier for rate changes i2c: make i2c_check_addr_validity() static i2c: rcar: fix mask value of prohibited bit dt-bindings: i2c: document R8A77965 bindings i2c: pca-platform: drop gpio from platform data i2c: pca-platform: use device_property_read_u32 i2c: pca-platform: unconditionally use devm_gpiod_get_optional sh: sh7785lcr: add GPIO lookup table for i2c controller reset i2c: qup: reorganization of driver code to remove polling for qup v2 i2c: qup: reorganization of driver code to remove polling for qup v1 i2c: qup: send NACK for last read sub transfers i2c: qup: fix buffer overflow for multiple msg of maximum xfer len i2c: qup: change completion timeout according to transfer length i2c: qup: use the complete transfer length to choose DMA mode i2c: qup: proper error handling for i2c error in BAM mode ...
This commit is contained in:
commit
0d5b1bd332
|
@ -41,12 +41,16 @@ Required properties:
|
|||
"nxp",
|
||||
"ramtron",
|
||||
"renesas",
|
||||
"rohm",
|
||||
"st",
|
||||
|
||||
Some vendors use different model names for chips which are just
|
||||
variants of the above. Known such exceptions are listed below:
|
||||
|
||||
"nxp,se97b" - the fallback is "atmel,24c02",
|
||||
"renesas,r1ex24002" - the fallback is "atmel,24c02"
|
||||
"renesas,r1ex24128" - the fallback is "atmel,24c128"
|
||||
"rohm,br24t01" - the fallback is "atmel,24c01"
|
||||
|
||||
- reg: The I2C address of the EEPROM.
|
||||
|
||||
|
|
|
@ -13,7 +13,9 @@ Required properties:
|
|||
"renesas,i2c-r8a7794" if the device is a part of a R8A7794 SoC.
|
||||
"renesas,i2c-r8a7795" if the device is a part of a R8A7795 SoC.
|
||||
"renesas,i2c-r8a7796" if the device is a part of a R8A7796 SoC.
|
||||
"renesas,i2c-r8a77965" if the device is a part of a R8A77965 SoC.
|
||||
"renesas,i2c-r8a77970" if the device is a part of a R8A77970 SoC.
|
||||
"renesas,i2c-r8a77995" if the device is a part of a R8A77995 SoC.
|
||||
"renesas,rcar-gen1-i2c" for a generic R-Car Gen1 compatible device.
|
||||
"renesas,rcar-gen2-i2c" for a generic R-Car Gen2 or RZ/G1 compatible
|
||||
device.
|
||||
|
|
|
@ -13,6 +13,7 @@ Required properties:
|
|||
- "renesas,iic-r8a7794" (R-Car E2)
|
||||
- "renesas,iic-r8a7795" (R-Car H3)
|
||||
- "renesas,iic-r8a7796" (R-Car M3-W)
|
||||
- "renesas,iic-r8a77965" (R-Car M3-N)
|
||||
- "renesas,iic-sh73a0" (SH-Mobile AG5)
|
||||
- "renesas,rcar-gen2-iic" (generic R-Car Gen2 or RZ/G1
|
||||
compatible device)
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
Socionext SynQuacer I2C
|
||||
|
||||
Required properties:
|
||||
- compatible : Must be "socionext,synquacer-i2c"
|
||||
- reg : Offset and length of the register set for the device
|
||||
- interrupts : A single interrupt specifier
|
||||
- #address-cells : Must be <1>;
|
||||
- #size-cells : Must be <0>;
|
||||
- clock-names : Must contain "pclk".
|
||||
- clocks : Must contain an entry for each name in clock-names.
|
||||
(See the common clock bindings.)
|
||||
|
||||
Optional properties:
|
||||
- clock-frequency : Desired I2C bus clock frequency in Hz. As only Normal and
|
||||
Fast modes are supported, possible values are 100000 and
|
||||
400000.
|
||||
|
||||
Example :
|
||||
|
||||
i2c@51210000 {
|
||||
compatible = "socionext,synquacer-i2c";
|
||||
reg = <0x51210000 0x1000>;
|
||||
interrupts = <GIC_SPI 165 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clock-names = "pclk";
|
||||
clocks = <&clk_i2c>;
|
||||
clock-frequency = <400000>;
|
||||
};
|
|
@ -6559,7 +6559,7 @@ F: drivers/i2c/muxes/
|
|||
F: include/linux/i2c-mux.h
|
||||
|
||||
I2C MV64XXX MARVELL AND ALLWINNER DRIVER
|
||||
M: Gregory CLEMENT <gregory.clement@free-electrons.com>
|
||||
M: Gregory CLEMENT <gregory.clement@bootlin.com>
|
||||
L: linux-i2c@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/i2c/busses/i2c-mv64xxx.c
|
||||
|
@ -12902,6 +12902,13 @@ F: include/media/soc*
|
|||
F: drivers/media/i2c/soc_camera/
|
||||
F: drivers/media/platform/soc_camera/
|
||||
|
||||
SOCIONEXT SYNQUACER I2C DRIVER
|
||||
M: Ard Biesheuvel <ard.biesheuvel@linaro.org>
|
||||
L: linux-i2c@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/i2c/busses/i2c-synquacer.c
|
||||
F: Documentation/devicetree/bindings/i2c/i2c-synquacer.txt
|
||||
|
||||
SOCIONEXT UNIPHIER SOUND DRIVER
|
||||
M: Katsuhiro Suzuki <suzuki.katsuhiro@socionext.com>
|
||||
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
#include <mach/sh7785lcr.h>
|
||||
#include <cpu/sh7785.h>
|
||||
#include <asm/heartbeat.h>
|
||||
|
@ -243,8 +244,15 @@ static struct resource i2c_resources[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static struct gpiod_lookup_table i2c_gpio_table = {
|
||||
.dev_id = "i2c.0",
|
||||
.table = {
|
||||
GPIO_LOOKUP("pfc-sh7757", 0, "reset-gpios", GPIO_ACTIVE_LOW),
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
static struct i2c_pca9564_pf_platform_data i2c_platform_data = {
|
||||
.gpio = 0,
|
||||
.i2c_clock_speed = I2C_PCA_CON_330kHz,
|
||||
.timeout = HZ,
|
||||
};
|
||||
|
@ -283,6 +291,7 @@ static int __init sh7785lcr_devices_setup(void)
|
|||
i2c_device.num_resources = ARRAY_SIZE(i2c_proto_resources);
|
||||
}
|
||||
|
||||
gpiod_add_lookup_table(&i2c_gpio_table);
|
||||
return platform_add_devices(sh7785lcr_devices,
|
||||
ARRAY_SIZE(sh7785lcr_devices));
|
||||
}
|
||||
|
|
|
@ -979,6 +979,16 @@ config I2C_SUN6I_P2WI
|
|||
This interface is used to connect to specific PMIC devices (like the
|
||||
AXP221).
|
||||
|
||||
config I2C_SYNQUACER
|
||||
tristate "Socionext SynQuacer I2C controller"
|
||||
depends on ARCH_SYNQUACER || COMPILE_TEST
|
||||
help
|
||||
Say Y here to include support for the I2C controller used in some
|
||||
Fujitsu and Socionext SoCs.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-synquacer.
|
||||
|
||||
config I2C_TEGRA
|
||||
tristate "NVIDIA Tegra internal I2C controller"
|
||||
depends on ARCH_TEGRA
|
||||
|
|
|
@ -97,6 +97,7 @@ obj-$(CONFIG_I2C_STM32F4) += i2c-stm32f4.o
|
|||
obj-$(CONFIG_I2C_STM32F7) += i2c-stm32f7.o
|
||||
obj-$(CONFIG_I2C_STU300) += i2c-stu300.o
|
||||
obj-$(CONFIG_I2C_SUN6I_P2WI) += i2c-sun6i-p2wi.o
|
||||
obj-$(CONFIG_I2C_SYNQUACER) += i2c-synquacer.o
|
||||
obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o
|
||||
obj-$(CONFIG_I2C_TEGRA_BPMP) += i2c-tegra-bpmp.o
|
||||
obj-$(CONFIG_I2C_UNIPHIER) += i2c-uniphier.o
|
||||
|
|
|
@ -163,7 +163,7 @@ static int i2c_dw_init_master(struct dw_i2c_dev *dev)
|
|||
if (!(dev->sda_hold_time & DW_IC_SDA_HOLD_RX_MASK))
|
||||
dev->sda_hold_time |= 1 << DW_IC_SDA_HOLD_RX_SHIFT;
|
||||
dw_writel(dev, dev->sda_hold_time, DW_IC_SDA_HOLD);
|
||||
} else {
|
||||
} else if (dev->sda_hold_time) {
|
||||
dev_warn(dev->dev,
|
||||
"Hardware too old to adjust SDA hold time.\n");
|
||||
}
|
||||
|
|
|
@ -128,6 +128,10 @@
|
|||
#define HSI2C_TIMEOUT_EN (1u << 31)
|
||||
#define HSI2C_TIMEOUT_MASK 0xff
|
||||
|
||||
/* I2C_MANUAL_CMD register bits */
|
||||
#define HSI2C_CMD_READ_DATA (1u << 4)
|
||||
#define HSI2C_CMD_SEND_STOP (1u << 2)
|
||||
|
||||
/* I2C_TRANS_STATUS register bits */
|
||||
#define HSI2C_MASTER_BUSY (1u << 17)
|
||||
#define HSI2C_SLAVE_BUSY (1u << 16)
|
||||
|
@ -441,12 +445,6 @@ static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id)
|
|||
i2c->state = -ETIMEDOUT;
|
||||
goto stop;
|
||||
}
|
||||
|
||||
trans_status = readl(i2c->regs + HSI2C_TRANS_STATUS);
|
||||
if ((trans_status & HSI2C_MASTER_ST_MASK) == HSI2C_MASTER_ST_LOSE) {
|
||||
i2c->state = -EAGAIN;
|
||||
goto stop;
|
||||
}
|
||||
} else if (int_status & HSI2C_INT_I2C) {
|
||||
trans_status = readl(i2c->regs + HSI2C_TRANS_STATUS);
|
||||
if (trans_status & HSI2C_NO_DEV_ACK) {
|
||||
|
@ -544,6 +542,57 @@ static int exynos5_i2c_wait_bus_idle(struct exynos5_i2c *i2c)
|
|||
return -EBUSY;
|
||||
}
|
||||
|
||||
static void exynos5_i2c_bus_recover(struct exynos5_i2c *i2c)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(i2c->regs + HSI2C_CTL) | HSI2C_RXCHON;
|
||||
writel(val, i2c->regs + HSI2C_CTL);
|
||||
val = readl(i2c->regs + HSI2C_CONF) & ~HSI2C_AUTO_MODE;
|
||||
writel(val, i2c->regs + HSI2C_CONF);
|
||||
|
||||
/*
|
||||
* Specification says master should send nine clock pulses. It can be
|
||||
* emulated by sending manual read command (nine pulses for read eight
|
||||
* bits + one pulse for NACK).
|
||||
*/
|
||||
writel(HSI2C_CMD_READ_DATA, i2c->regs + HSI2C_MANUAL_CMD);
|
||||
exynos5_i2c_wait_bus_idle(i2c);
|
||||
writel(HSI2C_CMD_SEND_STOP, i2c->regs + HSI2C_MANUAL_CMD);
|
||||
exynos5_i2c_wait_bus_idle(i2c);
|
||||
|
||||
val = readl(i2c->regs + HSI2C_CTL) & ~HSI2C_RXCHON;
|
||||
writel(val, i2c->regs + HSI2C_CTL);
|
||||
val = readl(i2c->regs + HSI2C_CONF) | HSI2C_AUTO_MODE;
|
||||
writel(val, i2c->regs + HSI2C_CONF);
|
||||
}
|
||||
|
||||
static void exynos5_i2c_bus_check(struct exynos5_i2c *i2c)
|
||||
{
|
||||
unsigned long timeout;
|
||||
|
||||
if (i2c->variant->hw != HSI2C_EXYNOS7)
|
||||
return;
|
||||
|
||||
/*
|
||||
* HSI2C_MASTER_ST_LOSE state in EXYNOS7 variant before transaction
|
||||
* indicates that bus is stuck (SDA is low). In such case bus recovery
|
||||
* can be performed.
|
||||
*/
|
||||
timeout = jiffies + msecs_to_jiffies(100);
|
||||
for (;;) {
|
||||
u32 st = readl(i2c->regs + HSI2C_TRANS_STATUS);
|
||||
|
||||
if ((st & HSI2C_MASTER_ST_MASK) != HSI2C_MASTER_ST_LOSE)
|
||||
return;
|
||||
|
||||
if (time_is_before_jiffies(timeout))
|
||||
return;
|
||||
|
||||
exynos5_i2c_bus_recover(i2c);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* exynos5_i2c_message_start: Configures the bus and starts the xfer
|
||||
* i2c: struct exynos5_i2c pointer for the current bus
|
||||
|
@ -598,6 +647,8 @@ static void exynos5_i2c_message_start(struct exynos5_i2c *i2c, int stop)
|
|||
writel(fifo_ctl, i2c->regs + HSI2C_FIFO_CTL);
|
||||
writel(i2c_ctl, i2c->regs + HSI2C_CTL);
|
||||
|
||||
exynos5_i2c_bus_check(i2c);
|
||||
|
||||
/*
|
||||
* Enable interrupts before starting the transfer so that we don't
|
||||
* miss any INT_I2C interrupts.
|
||||
|
|
|
@ -194,6 +194,7 @@ struct imx_i2c_dma {
|
|||
struct imx_i2c_struct {
|
||||
struct i2c_adapter adapter;
|
||||
struct clk *clk;
|
||||
struct notifier_block clk_change_nb;
|
||||
void __iomem *base;
|
||||
wait_queue_head_t queue;
|
||||
unsigned long i2csr;
|
||||
|
@ -467,15 +468,14 @@ static int i2c_imx_acked(struct imx_i2c_struct *i2c_imx)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx)
|
||||
static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx,
|
||||
unsigned int i2c_clk_rate)
|
||||
{
|
||||
struct imx_i2c_clk_pair *i2c_clk_div = i2c_imx->hwdata->clk_div;
|
||||
unsigned int i2c_clk_rate;
|
||||
unsigned int div;
|
||||
int i;
|
||||
|
||||
/* Divider value calculation */
|
||||
i2c_clk_rate = clk_get_rate(i2c_imx->clk);
|
||||
if (i2c_imx->cur_clk == i2c_clk_rate)
|
||||
return;
|
||||
|
||||
|
@ -510,6 +510,20 @@ static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx)
|
|||
#endif
|
||||
}
|
||||
|
||||
static int i2c_imx_clk_notifier_call(struct notifier_block *nb,
|
||||
unsigned long action, void *data)
|
||||
{
|
||||
struct clk_notifier_data *ndata = data;
|
||||
struct imx_i2c_struct *i2c_imx = container_of(&ndata->clk,
|
||||
struct imx_i2c_struct,
|
||||
clk);
|
||||
|
||||
if (action & POST_RATE_CHANGE)
|
||||
i2c_imx_set_clk(i2c_imx, ndata->new_rate);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int i2c_imx_start(struct imx_i2c_struct *i2c_imx)
|
||||
{
|
||||
unsigned int temp = 0;
|
||||
|
@ -517,8 +531,6 @@ static int i2c_imx_start(struct imx_i2c_struct *i2c_imx)
|
|||
|
||||
dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
|
||||
|
||||
i2c_imx_set_clk(i2c_imx);
|
||||
|
||||
imx_i2c_write_reg(i2c_imx->ifdr, i2c_imx, IMX_I2C_IFDR);
|
||||
/* Enable I2C controller */
|
||||
imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);
|
||||
|
@ -1131,6 +1143,9 @@ static int i2c_imx_probe(struct platform_device *pdev)
|
|||
"clock-frequency", &i2c_imx->bitrate);
|
||||
if (ret < 0 && pdata && pdata->bitrate)
|
||||
i2c_imx->bitrate = pdata->bitrate;
|
||||
i2c_imx->clk_change_nb.notifier_call = i2c_imx_clk_notifier_call;
|
||||
clk_notifier_register(i2c_imx->clk, &i2c_imx->clk_change_nb);
|
||||
i2c_imx_set_clk(i2c_imx, clk_get_rate(i2c_imx->clk));
|
||||
|
||||
/* Set up chip registers to defaults */
|
||||
imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
|
||||
|
@ -1141,12 +1156,12 @@ static int i2c_imx_probe(struct platform_device *pdev)
|
|||
ret = i2c_imx_init_recovery_info(i2c_imx, pdev);
|
||||
/* Give it another chance if pinctrl used is not ready yet */
|
||||
if (ret == -EPROBE_DEFER)
|
||||
goto rpm_disable;
|
||||
goto clk_notifier_unregister;
|
||||
|
||||
/* Add I2C adapter */
|
||||
ret = i2c_add_numbered_adapter(&i2c_imx->adapter);
|
||||
if (ret < 0)
|
||||
goto rpm_disable;
|
||||
goto clk_notifier_unregister;
|
||||
|
||||
pm_runtime_mark_last_busy(&pdev->dev);
|
||||
pm_runtime_put_autosuspend(&pdev->dev);
|
||||
|
@ -1162,6 +1177,8 @@ static int i2c_imx_probe(struct platform_device *pdev)
|
|||
|
||||
return 0; /* Return OK */
|
||||
|
||||
clk_notifier_unregister:
|
||||
clk_notifier_unregister(i2c_imx->clk, &i2c_imx->clk_change_nb);
|
||||
rpm_disable:
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
@ -1195,6 +1212,7 @@ static int i2c_imx_remove(struct platform_device *pdev)
|
|||
imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2CR);
|
||||
imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2SR);
|
||||
|
||||
clk_notifier_unregister(i2c_imx->clk, &i2c_imx->clk_change_nb);
|
||||
clk_disable_unprepare(i2c_imx->clk);
|
||||
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
|
@ -1208,7 +1226,7 @@ static int i2c_imx_runtime_suspend(struct device *dev)
|
|||
{
|
||||
struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable_unprepare(i2c_imx->clk);
|
||||
clk_disable(i2c_imx->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1218,7 +1236,7 @@ static int i2c_imx_runtime_resume(struct device *dev)
|
|||
struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(i2c_imx->clk);
|
||||
ret = clk_enable(i2c_imx->clk);
|
||||
if (ret)
|
||||
dev_err(dev, "can't enable I2C clock, ret=%d\n", ret);
|
||||
|
||||
|
|
|
@ -845,12 +845,16 @@ mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
|
|||
*/
|
||||
if (of_device_is_compatible(np, "marvell,mv78230-i2c")) {
|
||||
drv_data->offload_enabled = true;
|
||||
drv_data->errata_delay = true;
|
||||
/* The delay is only needed in standard mode (100kHz) */
|
||||
if (bus_freq <= 100000)
|
||||
drv_data->errata_delay = true;
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(np, "marvell,mv78230-a0-i2c")) {
|
||||
drv_data->offload_enabled = false;
|
||||
drv_data->errata_delay = true;
|
||||
/* The delay is only needed in standard mode (100kHz) */
|
||||
if (bus_freq <= 100000)
|
||||
drv_data->errata_delay = true;
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(np, "allwinner,sun6i-a31-i2c"))
|
||||
|
|
|
@ -173,33 +173,19 @@ static int i2c_pca_pf_probe(struct platform_device *pdev)
|
|||
i2c->adap.dev.parent = &pdev->dev;
|
||||
i2c->adap.dev.of_node = np;
|
||||
|
||||
i2c->gpio = devm_gpiod_get_optional(&pdev->dev, "reset-gpios", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(i2c->gpio))
|
||||
return PTR_ERR(i2c->gpio);
|
||||
|
||||
i2c->adap.timeout = HZ;
|
||||
ret = device_property_read_u32(&pdev->dev, "clock-frequency",
|
||||
&i2c->algo_data.i2c_clock);
|
||||
if (ret)
|
||||
i2c->algo_data.i2c_clock = 59000;
|
||||
|
||||
if (platform_data) {
|
||||
i2c->adap.timeout = platform_data->timeout;
|
||||
i2c->algo_data.i2c_clock = platform_data->i2c_clock_speed;
|
||||
if (gpio_is_valid(platform_data->gpio)) {
|
||||
ret = devm_gpio_request_one(&pdev->dev,
|
||||
platform_data->gpio,
|
||||
GPIOF_ACTIVE_LOW,
|
||||
i2c->adap.name);
|
||||
if (ret == 0) {
|
||||
i2c->gpio = gpio_to_desc(platform_data->gpio);
|
||||
gpiod_direction_output(i2c->gpio, 0);
|
||||
} else {
|
||||
dev_warn(&pdev->dev, "Registering gpio failed!\n");
|
||||
i2c->gpio = NULL;
|
||||
}
|
||||
}
|
||||
} else if (np) {
|
||||
i2c->adap.timeout = HZ;
|
||||
i2c->gpio = devm_gpiod_get_optional(&pdev->dev, "reset-gpios", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(i2c->gpio))
|
||||
return PTR_ERR(i2c->gpio);
|
||||
of_property_read_u32_index(np, "clock-frequency", 0,
|
||||
&i2c->algo_data.i2c_clock);
|
||||
} else {
|
||||
i2c->adap.timeout = HZ;
|
||||
i2c->algo_data.i2c_clock = 59000;
|
||||
i2c->gpio = NULL;
|
||||
}
|
||||
|
||||
i2c->algo_data.data = i2c;
|
||||
|
|
|
@ -40,7 +40,6 @@
|
|||
#include <linux/dmi.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
|
||||
/* PIIX4 SMBus address offsets */
|
||||
|
@ -153,10 +152,7 @@ static const struct dmi_system_id piix4_dmi_ibm[] = {
|
|||
|
||||
/*
|
||||
* SB800 globals
|
||||
* piix4_mutex_sb800 protects piix4_port_sel_sb800 and the pair
|
||||
* of I/O ports at SB800_PIIX4_SMB_IDX.
|
||||
*/
|
||||
static DEFINE_MUTEX(piix4_mutex_sb800);
|
||||
static u8 piix4_port_sel_sb800;
|
||||
static u8 piix4_port_mask_sb800;
|
||||
static u8 piix4_port_shift_sb800;
|
||||
|
@ -298,12 +294,19 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
|
|||
else
|
||||
smb_en = (aux) ? 0x28 : 0x2c;
|
||||
|
||||
mutex_lock(&piix4_mutex_sb800);
|
||||
if (!request_muxed_region(SB800_PIIX4_SMB_IDX, 2, "sb800_piix4_smb")) {
|
||||
dev_err(&PIIX4_dev->dev,
|
||||
"SMB base address index region 0x%x already in use.\n",
|
||||
SB800_PIIX4_SMB_IDX);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
outb_p(smb_en, SB800_PIIX4_SMB_IDX);
|
||||
smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1);
|
||||
outb_p(smb_en + 1, SB800_PIIX4_SMB_IDX);
|
||||
smba_en_hi = inb_p(SB800_PIIX4_SMB_IDX + 1);
|
||||
mutex_unlock(&piix4_mutex_sb800);
|
||||
|
||||
release_region(SB800_PIIX4_SMB_IDX, 2);
|
||||
|
||||
if (!smb_en) {
|
||||
smb_en_status = smba_en_lo & 0x10;
|
||||
|
@ -373,7 +376,12 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
|
|||
break;
|
||||
}
|
||||
} else {
|
||||
mutex_lock(&piix4_mutex_sb800);
|
||||
if (!request_muxed_region(SB800_PIIX4_SMB_IDX, 2,
|
||||
"sb800_piix4_smb")) {
|
||||
release_region(piix4_smba, SMBIOSIZE);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
outb_p(SB800_PIIX4_PORT_IDX_SEL, SB800_PIIX4_SMB_IDX);
|
||||
port_sel = inb_p(SB800_PIIX4_SMB_IDX + 1);
|
||||
piix4_port_sel_sb800 = (port_sel & 0x01) ?
|
||||
|
@ -381,7 +389,7 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
|
|||
SB800_PIIX4_PORT_IDX;
|
||||
piix4_port_mask_sb800 = SB800_PIIX4_PORT_IDX_MASK;
|
||||
piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT;
|
||||
mutex_unlock(&piix4_mutex_sb800);
|
||||
release_region(SB800_PIIX4_SMB_IDX, 2);
|
||||
}
|
||||
|
||||
dev_info(&PIIX4_dev->dev,
|
||||
|
@ -462,13 +470,13 @@ static int piix4_transaction(struct i2c_adapter *piix4_adapter)
|
|||
|
||||
/* We will always wait for a fraction of a second! (See PIIX4 docs errata) */
|
||||
if (srvrworks_csb5_delay) /* Extra delay for SERVERWORKS_CSB5 */
|
||||
msleep(2);
|
||||
usleep_range(2000, 2100);
|
||||
else
|
||||
msleep(1);
|
||||
usleep_range(250, 500);
|
||||
|
||||
while ((++timeout < MAX_TIMEOUT) &&
|
||||
((temp = inb_p(SMBHSTSTS)) & 0x01))
|
||||
msleep(1);
|
||||
usleep_range(250, 500);
|
||||
|
||||
/* If the SMBus is still busy, we give up */
|
||||
if (timeout == MAX_TIMEOUT) {
|
||||
|
@ -679,7 +687,8 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr,
|
|||
u8 port;
|
||||
int retval;
|
||||
|
||||
mutex_lock(&piix4_mutex_sb800);
|
||||
if (!request_muxed_region(SB800_PIIX4_SMB_IDX, 2, "sb800_piix4_smb"))
|
||||
return -EBUSY;
|
||||
|
||||
/* Request the SMBUS semaphore, avoid conflicts with the IMC */
|
||||
smbslvcnt = inb_p(SMBSLVCNT);
|
||||
|
@ -695,8 +704,8 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr,
|
|||
} while (--retries);
|
||||
/* SMBus is still owned by the IMC, we give up */
|
||||
if (!retries) {
|
||||
mutex_unlock(&piix4_mutex_sb800);
|
||||
return -EBUSY;
|
||||
retval = -EBUSY;
|
||||
goto release;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -753,8 +762,8 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr,
|
|||
if ((size == I2C_SMBUS_BLOCK_DATA) && adapdata->notify_imc)
|
||||
piix4_imc_wakeup();
|
||||
|
||||
mutex_unlock(&piix4_mutex_sb800);
|
||||
|
||||
release:
|
||||
release_region(SB800_PIIX4_SMB_IDX, 2);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
@ -899,13 +908,6 @@ static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|||
bool notify_imc = false;
|
||||
is_sb800 = true;
|
||||
|
||||
if (!request_region(SB800_PIIX4_SMB_IDX, 2, "smba_idx")) {
|
||||
dev_err(&dev->dev,
|
||||
"SMBus base address index region 0x%x already in use!\n",
|
||||
SB800_PIIX4_SMB_IDX);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (dev->vendor == PCI_VENDOR_ID_AMD &&
|
||||
dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS) {
|
||||
u8 imc;
|
||||
|
@ -922,20 +924,16 @@ static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|||
|
||||
/* base address location etc changed in SB800 */
|
||||
retval = piix4_setup_sb800(dev, id, 0);
|
||||
if (retval < 0) {
|
||||
release_region(SB800_PIIX4_SMB_IDX, 2);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to register multiplexed main SMBus adapter,
|
||||
* give up if we can't
|
||||
*/
|
||||
retval = piix4_add_adapters_sb800(dev, retval, notify_imc);
|
||||
if (retval < 0) {
|
||||
release_region(SB800_PIIX4_SMB_IDX, 2);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
}
|
||||
} else {
|
||||
retval = piix4_setup(dev, id);
|
||||
if (retval < 0)
|
||||
|
@ -983,11 +981,8 @@ static void piix4_adap_remove(struct i2c_adapter *adap)
|
|||
|
||||
if (adapdata->smba) {
|
||||
i2c_del_adapter(adap);
|
||||
if (adapdata->port == (0 << piix4_port_shift_sb800)) {
|
||||
if (adapdata->port == (0 << piix4_port_shift_sb800))
|
||||
release_region(adapdata->smba, SMBIOSIZE);
|
||||
if (adapdata->sb800_main)
|
||||
release_region(SB800_PIIX4_SMB_IDX, 2);
|
||||
}
|
||||
kfree(adapdata);
|
||||
kfree(adap);
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -102,8 +102,8 @@
|
|||
#define RCAR_IRQ_RECV (MNR | MAL | MST | MAT | MDR)
|
||||
#define RCAR_IRQ_STOP (MST)
|
||||
|
||||
#define RCAR_IRQ_ACK_SEND (~(MAT | MDE) & 0xFF)
|
||||
#define RCAR_IRQ_ACK_RECV (~(MAT | MDR) & 0xFF)
|
||||
#define RCAR_IRQ_ACK_SEND (~(MAT | MDE) & 0x7F)
|
||||
#define RCAR_IRQ_ACK_RECV (~(MAT | MDR) & 0x7F)
|
||||
|
||||
#define ID_LAST_MSG (1 << 0)
|
||||
#define ID_FIRST_MSG (1 << 1)
|
||||
|
|
|
@ -182,7 +182,8 @@ acpi_smbus_cmi_access(struct i2c_adapter *adap, u16 addr, unsigned short flags,
|
|||
status = acpi_evaluate_object(smbus_cmi->handle, method, &input,
|
||||
&buffer);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
ACPI_ERROR((AE_INFO, "Evaluating %s: %i", method, status));
|
||||
acpi_handle_err(smbus_cmi->handle,
|
||||
"Failed to evaluate %s: %i\n", method, status);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
@ -190,19 +191,19 @@ acpi_smbus_cmi_access(struct i2c_adapter *adap, u16 addr, unsigned short flags,
|
|||
if (pkg && pkg->type == ACPI_TYPE_PACKAGE)
|
||||
obj = pkg->package.elements;
|
||||
else {
|
||||
ACPI_ERROR((AE_INFO, "Invalid argument type"));
|
||||
acpi_handle_err(smbus_cmi->handle, "Invalid argument type\n");
|
||||
result = -EIO;
|
||||
goto out;
|
||||
}
|
||||
if (obj == NULL || obj->type != ACPI_TYPE_INTEGER) {
|
||||
ACPI_ERROR((AE_INFO, "Invalid argument type"));
|
||||
acpi_handle_err(smbus_cmi->handle, "Invalid argument type\n");
|
||||
result = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
result = obj->integer.value;
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s return status: %i\n",
|
||||
method, result));
|
||||
acpi_handle_debug(smbus_cmi->handle, "%s return status: %i\n", method,
|
||||
result);
|
||||
|
||||
switch (result) {
|
||||
case ACPI_SMBUS_STATUS_OK:
|
||||
|
@ -227,7 +228,7 @@ acpi_smbus_cmi_access(struct i2c_adapter *adap, u16 addr, unsigned short flags,
|
|||
|
||||
obj = pkg->package.elements + 1;
|
||||
if (obj->type != ACPI_TYPE_INTEGER) {
|
||||
ACPI_ERROR((AE_INFO, "Invalid argument type"));
|
||||
acpi_handle_err(smbus_cmi->handle, "Invalid argument type\n");
|
||||
result = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
@ -239,7 +240,8 @@ acpi_smbus_cmi_access(struct i2c_adapter *adap, u16 addr, unsigned short flags,
|
|||
case I2C_SMBUS_BYTE_DATA:
|
||||
case I2C_SMBUS_WORD_DATA:
|
||||
if (obj->type != ACPI_TYPE_INTEGER) {
|
||||
ACPI_ERROR((AE_INFO, "Invalid argument type"));
|
||||
acpi_handle_err(smbus_cmi->handle,
|
||||
"Invalid argument type\n");
|
||||
result = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
@ -250,7 +252,8 @@ acpi_smbus_cmi_access(struct i2c_adapter *adap, u16 addr, unsigned short flags,
|
|||
break;
|
||||
case I2C_SMBUS_BLOCK_DATA:
|
||||
if (obj->type != ACPI_TYPE_BUFFER) {
|
||||
ACPI_ERROR((AE_INFO, "Invalid argument type"));
|
||||
acpi_handle_err(smbus_cmi->handle,
|
||||
"Invalid argument type\n");
|
||||
result = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
@ -300,6 +303,7 @@ static int acpi_smbus_cmi_add_cap(struct acpi_smbus_cmi *smbus_cmi,
|
|||
const char *name)
|
||||
{
|
||||
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
struct acpi_handle *handle = smbus_cmi->handle;
|
||||
union acpi_object *obj;
|
||||
acpi_status status;
|
||||
|
||||
|
@ -308,8 +312,8 @@ static int acpi_smbus_cmi_add_cap(struct acpi_smbus_cmi *smbus_cmi,
|
|||
smbus_cmi->methods->mt_info,
|
||||
NULL, &buffer);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
ACPI_ERROR((AE_INFO, "Evaluating %s: %i",
|
||||
smbus_cmi->methods->mt_info, status));
|
||||
acpi_handle_err(handle, "Failed to evaluate %s: %i\n",
|
||||
smbus_cmi->methods->mt_info, status);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
@ -317,18 +321,18 @@ static int acpi_smbus_cmi_add_cap(struct acpi_smbus_cmi *smbus_cmi,
|
|||
if (obj && obj->type == ACPI_TYPE_PACKAGE)
|
||||
obj = obj->package.elements;
|
||||
else {
|
||||
ACPI_ERROR((AE_INFO, "Invalid argument type"));
|
||||
acpi_handle_err(handle, "Invalid argument type\n");
|
||||
kfree(buffer.pointer);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (obj->type != ACPI_TYPE_INTEGER) {
|
||||
ACPI_ERROR((AE_INFO, "Invalid argument type"));
|
||||
acpi_handle_err(handle, "Invalid argument type\n");
|
||||
kfree(buffer.pointer);
|
||||
return -EIO;
|
||||
} else
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "SMBus CMI Version %x"
|
||||
"\n", (int)obj->integer.value));
|
||||
acpi_handle_debug(handle, "SMBus CMI Version %x\n",
|
||||
(int)obj->integer.value);
|
||||
|
||||
kfree(buffer.pointer);
|
||||
smbus_cmi->cap_info = 1;
|
||||
|
@ -337,8 +341,7 @@ static int acpi_smbus_cmi_add_cap(struct acpi_smbus_cmi *smbus_cmi,
|
|||
else if (!strcmp(name, smbus_cmi->methods->mt_sbw))
|
||||
smbus_cmi->cap_write = 1;
|
||||
else
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Unsupported CMI method: %s\n",
|
||||
name));
|
||||
acpi_handle_debug(handle, "Unsupported CMI method: %s\n", name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -349,7 +349,7 @@ static void stm32f4_i2c_read_msg(struct stm32f4_i2c_dev *i2c_dev)
|
|||
static void stm32f4_i2c_terminate_xfer(struct stm32f4_i2c_dev *i2c_dev)
|
||||
{
|
||||
struct stm32f4_i2c_msg *msg = &i2c_dev->msg;
|
||||
void __iomem *reg = i2c_dev->base + STM32F4_I2C_CR2;
|
||||
void __iomem *reg;
|
||||
|
||||
stm32f4_i2c_disable_irq(i2c_dev);
|
||||
|
||||
|
|
|
@ -0,0 +1,667 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2012 FUJITSU SEMICONDUCTOR LIMITED
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#define WAIT_PCLK(n, rate) \
|
||||
ndelay(DIV_ROUND_UP(DIV_ROUND_UP(1000000000, rate), n) + 10)
|
||||
|
||||
/* I2C register address definitions */
|
||||
#define SYNQUACER_I2C_REG_BSR (0x00 << 2) // Bus Status
|
||||
#define SYNQUACER_I2C_REG_BCR (0x01 << 2) // Bus Control
|
||||
#define SYNQUACER_I2C_REG_CCR (0x02 << 2) // Clock Control
|
||||
#define SYNQUACER_I2C_REG_ADR (0x03 << 2) // Address
|
||||
#define SYNQUACER_I2C_REG_DAR (0x04 << 2) // Data
|
||||
#define SYNQUACER_I2C_REG_CSR (0x05 << 2) // Expansion CS
|
||||
#define SYNQUACER_I2C_REG_FSR (0x06 << 2) // Bus Clock Freq
|
||||
#define SYNQUACER_I2C_REG_BC2R (0x07 << 2) // Bus Control 2
|
||||
|
||||
/* I2C register bit definitions */
|
||||
#define SYNQUACER_I2C_BSR_FBT BIT(0) // First Byte Transfer
|
||||
#define SYNQUACER_I2C_BSR_GCA BIT(1) // General Call Address
|
||||
#define SYNQUACER_I2C_BSR_AAS BIT(2) // Address as Slave
|
||||
#define SYNQUACER_I2C_BSR_TRX BIT(3) // Transfer/Receive
|
||||
#define SYNQUACER_I2C_BSR_LRB BIT(4) // Last Received Bit
|
||||
#define SYNQUACER_I2C_BSR_AL BIT(5) // Arbitration Lost
|
||||
#define SYNQUACER_I2C_BSR_RSC BIT(6) // Repeated Start Cond.
|
||||
#define SYNQUACER_I2C_BSR_BB BIT(7) // Bus Busy
|
||||
|
||||
#define SYNQUACER_I2C_BCR_INT BIT(0) // Interrupt
|
||||
#define SYNQUACER_I2C_BCR_INTE BIT(1) // Interrupt Enable
|
||||
#define SYNQUACER_I2C_BCR_GCAA BIT(2) // Gen. Call Access Ack.
|
||||
#define SYNQUACER_I2C_BCR_ACK BIT(3) // Acknowledge
|
||||
#define SYNQUACER_I2C_BCR_MSS BIT(4) // Master Slave Select
|
||||
#define SYNQUACER_I2C_BCR_SCC BIT(5) // Start Condition Cont.
|
||||
#define SYNQUACER_I2C_BCR_BEIE BIT(6) // Bus Error Int Enable
|
||||
#define SYNQUACER_I2C_BCR_BER BIT(7) // Bus Error
|
||||
|
||||
#define SYNQUACER_I2C_CCR_CS_MASK (0x1f) // CCR Clock Period Sel.
|
||||
#define SYNQUACER_I2C_CCR_EN BIT(5) // Enable
|
||||
#define SYNQUACER_I2C_CCR_FM BIT(6) // Speed Mode Select
|
||||
|
||||
#define SYNQUACER_I2C_CSR_CS_MASK (0x3f) // CSR Clock Period Sel.
|
||||
|
||||
#define SYNQUACER_I2C_BC2R_SCLL BIT(0) // SCL Low Drive
|
||||
#define SYNQUACER_I2C_BC2R_SDAL BIT(1) // SDA Low Drive
|
||||
#define SYNQUACER_I2C_BC2R_SCLS BIT(4) // SCL Status
|
||||
#define SYNQUACER_I2C_BC2R_SDAS BIT(5) // SDA Status
|
||||
|
||||
/* PCLK frequency */
|
||||
#define SYNQUACER_I2C_BUS_CLK_FR(rate) (((rate) / 20000000) + 1)
|
||||
|
||||
/* STANDARD MODE frequency */
|
||||
#define SYNQUACER_I2C_CLK_MASTER_STD(rate) \
|
||||
DIV_ROUND_UP(DIV_ROUND_UP((rate), 100000) - 2, 2)
|
||||
/* FAST MODE frequency */
|
||||
#define SYNQUACER_I2C_CLK_MASTER_FAST(rate) \
|
||||
DIV_ROUND_UP((DIV_ROUND_UP((rate), 400000) - 2) * 2, 3)
|
||||
|
||||
/* (clkrate <= 18000000) */
|
||||
/* calculate the value of CS bits in CCR register on standard mode */
|
||||
#define SYNQUACER_I2C_CCR_CS_STD_MAX_18M(rate) \
|
||||
((SYNQUACER_I2C_CLK_MASTER_STD(rate) - 65) \
|
||||
& SYNQUACER_I2C_CCR_CS_MASK)
|
||||
|
||||
/* calculate the value of CS bits in CSR register on standard mode */
|
||||
#define SYNQUACER_I2C_CSR_CS_STD_MAX_18M(rate) 0x00
|
||||
|
||||
/* calculate the value of CS bits in CCR register on fast mode */
|
||||
#define SYNQUACER_I2C_CCR_CS_FAST_MAX_18M(rate) \
|
||||
((SYNQUACER_I2C_CLK_MASTER_FAST(rate) - 1) \
|
||||
& SYNQUACER_I2C_CCR_CS_MASK)
|
||||
|
||||
/* calculate the value of CS bits in CSR register on fast mode */
|
||||
#define SYNQUACER_I2C_CSR_CS_FAST_MAX_18M(rate) 0x00
|
||||
|
||||
/* (clkrate > 18000000) */
|
||||
/* calculate the value of CS bits in CCR register on standard mode */
|
||||
#define SYNQUACER_I2C_CCR_CS_STD_MIN_18M(rate) \
|
||||
((SYNQUACER_I2C_CLK_MASTER_STD(rate) - 1) \
|
||||
& SYNQUACER_I2C_CCR_CS_MASK)
|
||||
|
||||
/* calculate the value of CS bits in CSR register on standard mode */
|
||||
#define SYNQUACER_I2C_CSR_CS_STD_MIN_18M(rate) \
|
||||
(((SYNQUACER_I2C_CLK_MASTER_STD(rate) - 1) >> 5) \
|
||||
& SYNQUACER_I2C_CSR_CS_MASK)
|
||||
|
||||
/* calculate the value of CS bits in CCR register on fast mode */
|
||||
#define SYNQUACER_I2C_CCR_CS_FAST_MIN_18M(rate) \
|
||||
((SYNQUACER_I2C_CLK_MASTER_FAST(rate) - 1) \
|
||||
& SYNQUACER_I2C_CCR_CS_MASK)
|
||||
|
||||
/* calculate the value of CS bits in CSR register on fast mode */
|
||||
#define SYNQUACER_I2C_CSR_CS_FAST_MIN_18M(rate) \
|
||||
(((SYNQUACER_I2C_CLK_MASTER_FAST(rate) - 1) >> 5) \
|
||||
& SYNQUACER_I2C_CSR_CS_MASK)
|
||||
|
||||
/* min I2C clock frequency 14M */
|
||||
#define SYNQUACER_I2C_MIN_CLK_RATE (14 * 1000000)
|
||||
/* max I2C clock frequency 200M */
|
||||
#define SYNQUACER_I2C_MAX_CLK_RATE (200 * 1000000)
|
||||
/* I2C clock frequency 18M */
|
||||
#define SYNQUACER_I2C_CLK_RATE_18M (18 * 1000000)
|
||||
|
||||
#define SYNQUACER_I2C_SPEED_FM 400 // Fast Mode
|
||||
#define SYNQUACER_I2C_SPEED_SM 100 // Standard Mode
|
||||
|
||||
enum i2c_state {
|
||||
STATE_IDLE,
|
||||
STATE_START,
|
||||
STATE_READ,
|
||||
STATE_WRITE
|
||||
};
|
||||
|
||||
struct synquacer_i2c {
|
||||
struct completion completion;
|
||||
|
||||
struct i2c_msg *msg;
|
||||
u32 msg_num;
|
||||
u32 msg_idx;
|
||||
u32 msg_ptr;
|
||||
|
||||
int irq;
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
struct clk *pclk;
|
||||
u32 pclkrate;
|
||||
u32 speed_khz;
|
||||
u32 timeout_ms;
|
||||
enum i2c_state state;
|
||||
struct i2c_adapter adapter;
|
||||
|
||||
bool is_suspended;
|
||||
};
|
||||
|
||||
static inline int is_lastmsg(struct synquacer_i2c *i2c)
|
||||
{
|
||||
return i2c->msg_idx >= (i2c->msg_num - 1);
|
||||
}
|
||||
|
||||
static inline int is_msglast(struct synquacer_i2c *i2c)
|
||||
{
|
||||
return i2c->msg_ptr == (i2c->msg->len - 1);
|
||||
}
|
||||
|
||||
static inline int is_msgend(struct synquacer_i2c *i2c)
|
||||
{
|
||||
return i2c->msg_ptr >= i2c->msg->len;
|
||||
}
|
||||
|
||||
static inline unsigned long calc_timeout_ms(struct synquacer_i2c *i2c,
|
||||
struct i2c_msg *msgs,
|
||||
int num)
|
||||
{
|
||||
unsigned long bit_count = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num; i++, msgs++)
|
||||
bit_count += msgs->len;
|
||||
|
||||
return DIV_ROUND_UP((bit_count * 9 + num * 10) * 3, 200) + 10;
|
||||
}
|
||||
|
||||
static void synquacer_i2c_stop(struct synquacer_i2c *i2c, int ret)
|
||||
{
|
||||
/*
|
||||
* clear IRQ (INT=0, BER=0)
|
||||
* set Stop Condition (MSS=0)
|
||||
* Interrupt Disable
|
||||
*/
|
||||
writeb(0, i2c->base + SYNQUACER_I2C_REG_BCR);
|
||||
|
||||
i2c->state = STATE_IDLE;
|
||||
|
||||
i2c->msg_ptr = 0;
|
||||
i2c->msg = NULL;
|
||||
i2c->msg_idx++;
|
||||
i2c->msg_num = 0;
|
||||
if (ret)
|
||||
i2c->msg_idx = ret;
|
||||
|
||||
complete(&i2c->completion);
|
||||
}
|
||||
|
||||
static void synquacer_i2c_hw_init(struct synquacer_i2c *i2c)
|
||||
{
|
||||
unsigned char ccr_cs, csr_cs;
|
||||
u32 rt = i2c->pclkrate;
|
||||
|
||||
/* Set own Address */
|
||||
writeb(0, i2c->base + SYNQUACER_I2C_REG_ADR);
|
||||
|
||||
/* Set PCLK frequency */
|
||||
writeb(SYNQUACER_I2C_BUS_CLK_FR(i2c->pclkrate),
|
||||
i2c->base + SYNQUACER_I2C_REG_FSR);
|
||||
|
||||
switch (i2c->speed_khz) {
|
||||
case SYNQUACER_I2C_SPEED_FM:
|
||||
if (i2c->pclkrate <= SYNQUACER_I2C_CLK_RATE_18M) {
|
||||
ccr_cs = SYNQUACER_I2C_CCR_CS_FAST_MAX_18M(rt);
|
||||
csr_cs = SYNQUACER_I2C_CSR_CS_FAST_MAX_18M(rt);
|
||||
} else {
|
||||
ccr_cs = SYNQUACER_I2C_CCR_CS_FAST_MIN_18M(rt);
|
||||
csr_cs = SYNQUACER_I2C_CSR_CS_FAST_MIN_18M(rt);
|
||||
}
|
||||
|
||||
/* Set Clock and enable, Set fast mode */
|
||||
writeb(ccr_cs | SYNQUACER_I2C_CCR_FM |
|
||||
SYNQUACER_I2C_CCR_EN,
|
||||
i2c->base + SYNQUACER_I2C_REG_CCR);
|
||||
writeb(csr_cs, i2c->base + SYNQUACER_I2C_REG_CSR);
|
||||
break;
|
||||
case SYNQUACER_I2C_SPEED_SM:
|
||||
if (i2c->pclkrate <= SYNQUACER_I2C_CLK_RATE_18M) {
|
||||
ccr_cs = SYNQUACER_I2C_CCR_CS_STD_MAX_18M(rt);
|
||||
csr_cs = SYNQUACER_I2C_CSR_CS_STD_MAX_18M(rt);
|
||||
} else {
|
||||
ccr_cs = SYNQUACER_I2C_CCR_CS_STD_MIN_18M(rt);
|
||||
csr_cs = SYNQUACER_I2C_CSR_CS_STD_MIN_18M(rt);
|
||||
}
|
||||
|
||||
/* Set Clock and enable, Set standard mode */
|
||||
writeb(ccr_cs | SYNQUACER_I2C_CCR_EN,
|
||||
i2c->base + SYNQUACER_I2C_REG_CCR);
|
||||
writeb(csr_cs, i2c->base + SYNQUACER_I2C_REG_CSR);
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
}
|
||||
|
||||
/* clear IRQ (INT=0, BER=0), Interrupt Disable */
|
||||
writeb(0, i2c->base + SYNQUACER_I2C_REG_BCR);
|
||||
writeb(0, i2c->base + SYNQUACER_I2C_REG_BC2R);
|
||||
}
|
||||
|
||||
static void synquacer_i2c_hw_reset(struct synquacer_i2c *i2c)
|
||||
{
|
||||
/* Disable clock */
|
||||
writeb(0, i2c->base + SYNQUACER_I2C_REG_CCR);
|
||||
writeb(0, i2c->base + SYNQUACER_I2C_REG_CSR);
|
||||
|
||||
WAIT_PCLK(100, i2c->pclkrate);
|
||||
}
|
||||
|
||||
static int synquacer_i2c_master_start(struct synquacer_i2c *i2c,
|
||||
struct i2c_msg *pmsg)
|
||||
{
|
||||
unsigned char bsr, bcr;
|
||||
|
||||
writeb(i2c_8bit_addr_from_msg(pmsg), i2c->base + SYNQUACER_I2C_REG_DAR);
|
||||
|
||||
dev_dbg(i2c->dev, "slave:0x%02x\n", pmsg->addr);
|
||||
|
||||
/* Generate Start Condition */
|
||||
bsr = readb(i2c->base + SYNQUACER_I2C_REG_BSR);
|
||||
bcr = readb(i2c->base + SYNQUACER_I2C_REG_BCR);
|
||||
dev_dbg(i2c->dev, "bsr:0x%02x, bcr:0x%02x\n", bsr, bcr);
|
||||
|
||||
if ((bsr & SYNQUACER_I2C_BSR_BB) &&
|
||||
!(bcr & SYNQUACER_I2C_BCR_MSS)) {
|
||||
dev_dbg(i2c->dev, "bus is busy");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (bsr & SYNQUACER_I2C_BSR_BB) { /* Bus is busy */
|
||||
dev_dbg(i2c->dev, "Continuous Start");
|
||||
writeb(bcr | SYNQUACER_I2C_BCR_SCC,
|
||||
i2c->base + SYNQUACER_I2C_REG_BCR);
|
||||
} else {
|
||||
if (bcr & SYNQUACER_I2C_BCR_MSS) {
|
||||
dev_dbg(i2c->dev, "not in master mode");
|
||||
return -EAGAIN;
|
||||
}
|
||||
dev_dbg(i2c->dev, "Start Condition");
|
||||
/* Start Condition + Enable Interrupts */
|
||||
writeb(bcr | SYNQUACER_I2C_BCR_MSS |
|
||||
SYNQUACER_I2C_BCR_INTE | SYNQUACER_I2C_BCR_BEIE,
|
||||
i2c->base + SYNQUACER_I2C_REG_BCR);
|
||||
}
|
||||
|
||||
WAIT_PCLK(10, i2c->pclkrate);
|
||||
|
||||
/* get BSR & BCR registers */
|
||||
bsr = readb(i2c->base + SYNQUACER_I2C_REG_BSR);
|
||||
bcr = readb(i2c->base + SYNQUACER_I2C_REG_BCR);
|
||||
dev_dbg(i2c->dev, "bsr:0x%02x, bcr:0x%02x\n", bsr, bcr);
|
||||
|
||||
if ((bsr & SYNQUACER_I2C_BSR_AL) ||
|
||||
!(bcr & SYNQUACER_I2C_BCR_MSS)) {
|
||||
dev_dbg(i2c->dev, "arbitration lost\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int synquacer_i2c_doxfer(struct synquacer_i2c *i2c,
|
||||
struct i2c_msg *msgs, int num)
|
||||
{
|
||||
unsigned char bsr;
|
||||
unsigned long timeout;
|
||||
int ret;
|
||||
|
||||
if (i2c->is_suspended)
|
||||
return -EBUSY;
|
||||
|
||||
synquacer_i2c_hw_init(i2c);
|
||||
bsr = readb(i2c->base + SYNQUACER_I2C_REG_BSR);
|
||||
if (bsr & SYNQUACER_I2C_BSR_BB) {
|
||||
dev_err(i2c->dev, "cannot get bus (bus busy)\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
reinit_completion(&i2c->completion);
|
||||
|
||||
i2c->msg = msgs;
|
||||
i2c->msg_num = num;
|
||||
i2c->msg_ptr = 0;
|
||||
i2c->msg_idx = 0;
|
||||
i2c->state = STATE_START;
|
||||
|
||||
ret = synquacer_i2c_master_start(i2c, i2c->msg);
|
||||
if (ret < 0) {
|
||||
dev_dbg(i2c->dev, "Address failed: (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
timeout = wait_for_completion_timeout(&i2c->completion,
|
||||
msecs_to_jiffies(i2c->timeout_ms));
|
||||
if (timeout == 0) {
|
||||
dev_dbg(i2c->dev, "timeout\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
ret = i2c->msg_idx;
|
||||
if (ret != num) {
|
||||
dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/* wait 2 clock periods to ensure the stop has been through the bus */
|
||||
udelay(DIV_ROUND_UP(2 * 1000, i2c->speed_khz));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t synquacer_i2c_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct synquacer_i2c *i2c = dev_id;
|
||||
|
||||
unsigned char byte;
|
||||
unsigned char bsr, bcr;
|
||||
int ret;
|
||||
|
||||
bcr = readb(i2c->base + SYNQUACER_I2C_REG_BCR);
|
||||
bsr = readb(i2c->base + SYNQUACER_I2C_REG_BSR);
|
||||
dev_dbg(i2c->dev, "bsr:0x%02x, bcr:0x%02x\n", bsr, bcr);
|
||||
|
||||
if (bcr & SYNQUACER_I2C_BCR_BER) {
|
||||
dev_err(i2c->dev, "bus error\n");
|
||||
synquacer_i2c_stop(i2c, -EAGAIN);
|
||||
goto out;
|
||||
}
|
||||
if ((bsr & SYNQUACER_I2C_BSR_AL) ||
|
||||
!(bcr & SYNQUACER_I2C_BCR_MSS)) {
|
||||
dev_dbg(i2c->dev, "arbitration lost\n");
|
||||
synquacer_i2c_stop(i2c, -EAGAIN);
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (i2c->state) {
|
||||
case STATE_START:
|
||||
if (bsr & SYNQUACER_I2C_BSR_LRB) {
|
||||
dev_dbg(i2c->dev, "ack was not received\n");
|
||||
synquacer_i2c_stop(i2c, -EAGAIN);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (i2c->msg->flags & I2C_M_RD)
|
||||
i2c->state = STATE_READ;
|
||||
else
|
||||
i2c->state = STATE_WRITE;
|
||||
|
||||
if (is_lastmsg(i2c) && i2c->msg->len == 0) {
|
||||
synquacer_i2c_stop(i2c, 0);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (i2c->state == STATE_READ)
|
||||
goto prepare_read;
|
||||
|
||||
/* fallthru */
|
||||
|
||||
case STATE_WRITE:
|
||||
if (bsr & SYNQUACER_I2C_BSR_LRB) {
|
||||
dev_dbg(i2c->dev, "WRITE: No Ack\n");
|
||||
synquacer_i2c_stop(i2c, -EAGAIN);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!is_msgend(i2c)) {
|
||||
writeb(i2c->msg->buf[i2c->msg_ptr++],
|
||||
i2c->base + SYNQUACER_I2C_REG_DAR);
|
||||
|
||||
/* clear IRQ, and continue */
|
||||
writeb(SYNQUACER_I2C_BCR_BEIE |
|
||||
SYNQUACER_I2C_BCR_MSS |
|
||||
SYNQUACER_I2C_BCR_INTE,
|
||||
i2c->base + SYNQUACER_I2C_REG_BCR);
|
||||
break;
|
||||
}
|
||||
if (is_lastmsg(i2c)) {
|
||||
synquacer_i2c_stop(i2c, 0);
|
||||
break;
|
||||
}
|
||||
dev_dbg(i2c->dev, "WRITE: Next Message\n");
|
||||
|
||||
i2c->msg_ptr = 0;
|
||||
i2c->msg_idx++;
|
||||
i2c->msg++;
|
||||
|
||||
/* send the new start */
|
||||
ret = synquacer_i2c_master_start(i2c, i2c->msg);
|
||||
if (ret < 0) {
|
||||
dev_dbg(i2c->dev, "restart error (%d)\n", ret);
|
||||
synquacer_i2c_stop(i2c, -EAGAIN);
|
||||
break;
|
||||
}
|
||||
i2c->state = STATE_START;
|
||||
break;
|
||||
|
||||
case STATE_READ:
|
||||
byte = readb(i2c->base + SYNQUACER_I2C_REG_DAR);
|
||||
if (!(bsr & SYNQUACER_I2C_BSR_FBT)) /* data */
|
||||
i2c->msg->buf[i2c->msg_ptr++] = byte;
|
||||
else /* address */
|
||||
dev_dbg(i2c->dev, "address:0x%02x. ignore it.\n", byte);
|
||||
|
||||
prepare_read:
|
||||
if (is_msglast(i2c)) {
|
||||
writeb(SYNQUACER_I2C_BCR_MSS |
|
||||
SYNQUACER_I2C_BCR_BEIE |
|
||||
SYNQUACER_I2C_BCR_INTE,
|
||||
i2c->base + SYNQUACER_I2C_REG_BCR);
|
||||
break;
|
||||
}
|
||||
if (!is_msgend(i2c)) {
|
||||
writeb(SYNQUACER_I2C_BCR_MSS |
|
||||
SYNQUACER_I2C_BCR_BEIE |
|
||||
SYNQUACER_I2C_BCR_INTE |
|
||||
SYNQUACER_I2C_BCR_ACK,
|
||||
i2c->base + SYNQUACER_I2C_REG_BCR);
|
||||
break;
|
||||
}
|
||||
if (is_lastmsg(i2c)) {
|
||||
/* last message, send stop and complete */
|
||||
dev_dbg(i2c->dev, "READ: Send Stop\n");
|
||||
synquacer_i2c_stop(i2c, 0);
|
||||
break;
|
||||
}
|
||||
dev_dbg(i2c->dev, "READ: Next Transfer\n");
|
||||
|
||||
i2c->msg_ptr = 0;
|
||||
i2c->msg_idx++;
|
||||
i2c->msg++;
|
||||
|
||||
ret = synquacer_i2c_master_start(i2c, i2c->msg);
|
||||
if (ret < 0) {
|
||||
dev_dbg(i2c->dev, "restart error (%d)\n", ret);
|
||||
synquacer_i2c_stop(i2c, -EAGAIN);
|
||||
} else {
|
||||
i2c->state = STATE_START;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
dev_err(i2c->dev, "called in err STATE (%d)\n", i2c->state);
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
WAIT_PCLK(10, i2c->pclkrate);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int synquacer_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||
int num)
|
||||
{
|
||||
struct synquacer_i2c *i2c;
|
||||
int retry;
|
||||
int ret;
|
||||
|
||||
i2c = i2c_get_adapdata(adap);
|
||||
i2c->timeout_ms = calc_timeout_ms(i2c, msgs, num);
|
||||
|
||||
dev_dbg(i2c->dev, "calculated timeout %d ms\n", i2c->timeout_ms);
|
||||
|
||||
for (retry = 0; retry < adap->retries; retry++) {
|
||||
ret = synquacer_i2c_doxfer(i2c, msgs, num);
|
||||
if (ret != -EAGAIN)
|
||||
return ret;
|
||||
|
||||
dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);
|
||||
|
||||
synquacer_i2c_hw_reset(i2c);
|
||||
}
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static u32 synquacer_i2c_functionality(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm synquacer_i2c_algo = {
|
||||
.master_xfer = synquacer_i2c_xfer,
|
||||
.functionality = synquacer_i2c_functionality,
|
||||
};
|
||||
|
||||
static struct i2c_adapter synquacer_i2c_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "synquacer_i2c-adapter",
|
||||
.algo = &synquacer_i2c_algo,
|
||||
.retries = 5,
|
||||
};
|
||||
|
||||
static int synquacer_i2c_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct synquacer_i2c *i2c;
|
||||
struct resource *r;
|
||||
u32 bus_speed;
|
||||
int ret;
|
||||
|
||||
i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
|
||||
if (!i2c)
|
||||
return -ENOMEM;
|
||||
|
||||
bus_speed = i2c_acpi_find_bus_speed(&pdev->dev);
|
||||
if (!bus_speed)
|
||||
device_property_read_u32(&pdev->dev, "clock-frequency",
|
||||
&bus_speed);
|
||||
|
||||
device_property_read_u32(&pdev->dev, "socionext,pclk-rate",
|
||||
&i2c->pclkrate);
|
||||
|
||||
i2c->pclk = devm_clk_get(&pdev->dev, "pclk");
|
||||
if (IS_ERR(i2c->pclk) && PTR_ERR(i2c->pclk) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
if (!IS_ERR_OR_NULL(i2c->pclk)) {
|
||||
dev_dbg(&pdev->dev, "clock source %p\n", i2c->pclk);
|
||||
|
||||
ret = clk_prepare_enable(i2c->pclk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to enable clock (%d)\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
i2c->pclkrate = clk_get_rate(i2c->pclk);
|
||||
}
|
||||
|
||||
if (i2c->pclkrate < SYNQUACER_I2C_MIN_CLK_RATE ||
|
||||
i2c->pclkrate > SYNQUACER_I2C_MAX_CLK_RATE) {
|
||||
dev_err(&pdev->dev, "PCLK missing or out of range (%d)\n",
|
||||
i2c->pclkrate);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
i2c->base = devm_ioremap_resource(&pdev->dev, r);
|
||||
if (IS_ERR(i2c->base))
|
||||
return PTR_ERR(i2c->base);
|
||||
|
||||
i2c->irq = platform_get_irq(pdev, 0);
|
||||
if (i2c->irq < 0) {
|
||||
dev_err(&pdev->dev, "no IRQ resource found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, i2c->irq, synquacer_i2c_isr,
|
||||
0, dev_name(&pdev->dev), i2c);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
i2c->state = STATE_IDLE;
|
||||
i2c->dev = &pdev->dev;
|
||||
i2c->adapter = synquacer_i2c_ops;
|
||||
i2c_set_adapdata(&i2c->adapter, i2c);
|
||||
i2c->adapter.dev.parent = &pdev->dev;
|
||||
i2c->adapter.nr = pdev->id;
|
||||
init_completion(&i2c->completion);
|
||||
|
||||
if (bus_speed < 400000)
|
||||
i2c->speed_khz = SYNQUACER_I2C_SPEED_SM;
|
||||
else
|
||||
i2c->speed_khz = SYNQUACER_I2C_SPEED_FM;
|
||||
|
||||
synquacer_i2c_hw_init(i2c);
|
||||
|
||||
ret = i2c_add_numbered_adapter(&i2c->adapter);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add bus to i2c core\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, i2c);
|
||||
|
||||
dev_info(&pdev->dev, "%s: synquacer_i2c adapter\n",
|
||||
dev_name(&i2c->adapter.dev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int synquacer_i2c_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct synquacer_i2c *i2c = platform_get_drvdata(pdev);
|
||||
|
||||
i2c_del_adapter(&i2c->adapter);
|
||||
if (!IS_ERR(i2c->pclk))
|
||||
clk_disable_unprepare(i2c->pclk);
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
static const struct of_device_id synquacer_i2c_dt_ids[] = {
|
||||
{ .compatible = "socionext,synquacer-i2c" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, synquacer_i2c_dt_ids);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id synquacer_i2c_acpi_ids[] = {
|
||||
{ "SCX0003" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, synquacer_i2c_acpi_ids);
|
||||
#endif
|
||||
|
||||
static struct platform_driver synquacer_i2c_driver = {
|
||||
.probe = synquacer_i2c_probe,
|
||||
.remove = synquacer_i2c_remove,
|
||||
.driver = {
|
||||
.name = "synquacer_i2c",
|
||||
.of_match_table = of_match_ptr(synquacer_i2c_dt_ids),
|
||||
.acpi_match_table = ACPI_PTR(synquacer_i2c_acpi_ids),
|
||||
},
|
||||
};
|
||||
module_platform_driver(synquacer_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Fujitsu Semiconductor Ltd");
|
||||
MODULE_DESCRIPTION("Socionext SynQuacer I2C Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -851,7 +851,7 @@ static const struct of_device_id xiic_of_match[] = {
|
|||
MODULE_DEVICE_TABLE(of, xiic_of_match);
|
||||
#endif
|
||||
|
||||
static int __maybe_unused cdns_i2c_runtime_suspend(struct device *dev)
|
||||
static int __maybe_unused xiic_i2c_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct xiic_i2c *i2c = dev_get_drvdata(dev);
|
||||
|
||||
|
@ -860,7 +860,7 @@ static int __maybe_unused cdns_i2c_runtime_suspend(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused cdns_i2c_runtime_resume(struct device *dev)
|
||||
static int __maybe_unused xiic_i2c_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct xiic_i2c *i2c = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
@ -875,8 +875,8 @@ static int __maybe_unused cdns_i2c_runtime_resume(struct device *dev)
|
|||
}
|
||||
|
||||
static const struct dev_pm_ops xiic_dev_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(cdns_i2c_runtime_suspend,
|
||||
cdns_i2c_runtime_resume, NULL)
|
||||
SET_RUNTIME_PM_OPS(xiic_i2c_runtime_suspend,
|
||||
xiic_i2c_runtime_resume, NULL)
|
||||
};
|
||||
static struct platform_driver xiic_i2c_driver = {
|
||||
.probe = xiic_i2c_probe,
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#define XLP9XX_I2C_DIV 0x0
|
||||
#define XLP9XX_I2C_CTRL 0x1
|
||||
|
@ -36,6 +37,8 @@
|
|||
#define XLP9XX_I2C_TIMEOUT 0X10
|
||||
#define XLP9XX_I2C_GENCALLADDR 0x11
|
||||
|
||||
#define XLP9XX_I2C_STATUS_BUSY BIT(0)
|
||||
|
||||
#define XLP9XX_I2C_CMD_START BIT(7)
|
||||
#define XLP9XX_I2C_CMD_STOP BIT(6)
|
||||
#define XLP9XX_I2C_CMD_READ BIT(5)
|
||||
|
@ -71,6 +74,7 @@
|
|||
#define XLP9XX_I2C_HIGH_FREQ 400000
|
||||
#define XLP9XX_I2C_FIFO_SIZE 0x80U
|
||||
#define XLP9XX_I2C_TIMEOUT_MS 1000
|
||||
#define XLP9XX_I2C_BUSY_TIMEOUT 50
|
||||
|
||||
#define XLP9XX_I2C_FIFO_WCNT_MASK 0xff
|
||||
#define XLP9XX_I2C_STATUS_ERRMASK (XLP9XX_I2C_INTEN_ARLOST | \
|
||||
|
@ -125,7 +129,16 @@ static void xlp9xx_i2c_update_rx_fifo_thres(struct xlp9xx_i2c_dev *priv)
|
|||
{
|
||||
u32 thres;
|
||||
|
||||
thres = min(priv->msg_buf_remaining, XLP9XX_I2C_FIFO_SIZE);
|
||||
if (priv->len_recv)
|
||||
/* interrupt after the first read to examine
|
||||
* the length byte before proceeding further
|
||||
*/
|
||||
thres = 1;
|
||||
else if (priv->msg_buf_remaining > XLP9XX_I2C_FIFO_SIZE)
|
||||
thres = XLP9XX_I2C_FIFO_SIZE;
|
||||
else
|
||||
thres = priv->msg_buf_remaining;
|
||||
|
||||
xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_MFIFOCTRL,
|
||||
thres << XLP9XX_I2C_MFIFOCTRL_HITH_SHIFT);
|
||||
}
|
||||
|
@ -144,7 +157,7 @@ static void xlp9xx_i2c_fill_tx_fifo(struct xlp9xx_i2c_dev *priv)
|
|||
|
||||
static void xlp9xx_i2c_drain_rx_fifo(struct xlp9xx_i2c_dev *priv)
|
||||
{
|
||||
u32 len, i;
|
||||
u32 len, i, val;
|
||||
u8 rlen, *buf = priv->msg_buf;
|
||||
|
||||
len = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_FIFOWCNT) &
|
||||
|
@ -156,19 +169,27 @@ static void xlp9xx_i2c_drain_rx_fifo(struct xlp9xx_i2c_dev *priv)
|
|||
rlen = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_MRXFIFO);
|
||||
*buf++ = rlen;
|
||||
len--;
|
||||
|
||||
if (priv->client_pec)
|
||||
++rlen;
|
||||
/* update remaining bytes and message length */
|
||||
priv->msg_buf_remaining = rlen;
|
||||
priv->msg_len = rlen + 1;
|
||||
priv->len_recv = false;
|
||||
|
||||
/* Update transfer length to read only actual data */
|
||||
val = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_CTRL);
|
||||
val = (val & ~XLP9XX_I2C_CTRL_MCTLEN_MASK) |
|
||||
((rlen + 1) << XLP9XX_I2C_CTRL_MCTLEN_SHIFT);
|
||||
xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CTRL, val);
|
||||
} else {
|
||||
len = min(priv->msg_buf_remaining, len);
|
||||
for (i = 0; i < len; i++, buf++)
|
||||
*buf = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_MRXFIFO);
|
||||
|
||||
priv->msg_buf_remaining -= len;
|
||||
}
|
||||
|
||||
len = min(priv->msg_buf_remaining, len);
|
||||
for (i = 0; i < len; i++, buf++)
|
||||
*buf = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_MRXFIFO);
|
||||
|
||||
priv->msg_buf_remaining -= len;
|
||||
priv->msg_buf = buf;
|
||||
|
||||
if (priv->msg_buf_remaining)
|
||||
|
@ -224,6 +245,26 @@ static irqreturn_t xlp9xx_i2c_isr(int irq, void *dev_id)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int xlp9xx_i2c_check_bus_status(struct xlp9xx_i2c_dev *priv)
|
||||
{
|
||||
u32 status;
|
||||
u32 busy_timeout = XLP9XX_I2C_BUSY_TIMEOUT;
|
||||
|
||||
while (busy_timeout) {
|
||||
status = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_STATUS);
|
||||
if ((status & XLP9XX_I2C_STATUS_BUSY) == 0)
|
||||
break;
|
||||
|
||||
busy_timeout--;
|
||||
usleep_range(1000, 1100);
|
||||
}
|
||||
|
||||
if (!busy_timeout)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xlp9xx_i2c_init(struct xlp9xx_i2c_dev *priv)
|
||||
{
|
||||
u32 prescale;
|
||||
|
@ -311,7 +352,9 @@ static int xlp9xx_i2c_xfer_msg(struct xlp9xx_i2c_dev *priv, struct i2c_msg *msg,
|
|||
|
||||
/* set cmd reg */
|
||||
cmd = XLP9XX_I2C_CMD_START;
|
||||
cmd |= (priv->msg_read ? XLP9XX_I2C_CMD_READ : XLP9XX_I2C_CMD_WRITE);
|
||||
if (msg->len)
|
||||
cmd |= (priv->msg_read ?
|
||||
XLP9XX_I2C_CMD_READ : XLP9XX_I2C_CMD_WRITE);
|
||||
if (last_msg)
|
||||
cmd |= XLP9XX_I2C_CMD_STOP;
|
||||
|
||||
|
@ -320,11 +363,12 @@ static int xlp9xx_i2c_xfer_msg(struct xlp9xx_i2c_dev *priv, struct i2c_msg *msg,
|
|||
timeleft = msecs_to_jiffies(XLP9XX_I2C_TIMEOUT_MS);
|
||||
timeleft = wait_for_completion_timeout(&priv->msg_complete, timeleft);
|
||||
|
||||
if (priv->msg_err) {
|
||||
if (priv->msg_err & XLP9XX_I2C_INTEN_BUSERR) {
|
||||
dev_dbg(priv->dev, "transfer error %x!\n", priv->msg_err);
|
||||
if (priv->msg_err & XLP9XX_I2C_INTEN_BUSERR)
|
||||
xlp9xx_i2c_init(priv);
|
||||
xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CMD, XLP9XX_I2C_CMD_STOP);
|
||||
return -EIO;
|
||||
} else if (priv->msg_err & XLP9XX_I2C_INTEN_NACKADDR) {
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
if (timeleft == 0) {
|
||||
|
@ -345,6 +389,14 @@ static int xlp9xx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
|||
int i, ret;
|
||||
struct xlp9xx_i2c_dev *priv = i2c_get_adapdata(adap);
|
||||
|
||||
ret = xlp9xx_i2c_check_bus_status(priv);
|
||||
if (ret) {
|
||||
xlp9xx_i2c_init(priv);
|
||||
ret = xlp9xx_i2c_check_bus_status(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
ret = xlp9xx_i2c_xfer_msg(priv, &msgs[i], i == num - 1);
|
||||
if (ret != 0)
|
||||
|
@ -356,8 +408,8 @@ static int xlp9xx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
|||
|
||||
static u32 xlp9xx_i2c_functionality(struct i2c_adapter *adapter)
|
||||
{
|
||||
return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C |
|
||||
I2C_FUNC_10BIT_ADDR;
|
||||
return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_READ_BLOCK_DATA |
|
||||
I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm xlp9xx_i2c_algo = {
|
||||
|
|
|
@ -58,6 +58,8 @@
|
|||
#define I2C_ADDR_7BITS_MAX 0x77
|
||||
#define I2C_ADDR_7BITS_COUNT (I2C_ADDR_7BITS_MAX + 1)
|
||||
|
||||
#define I2C_ADDR_DEVICE_ID 0x7c
|
||||
|
||||
/*
|
||||
* core_lock protects i2c_adapter_idr, and guarantees that device detection,
|
||||
* deletion of detected devices, and attach_adapter calls are serialized
|
||||
|
@ -67,18 +69,18 @@ static DEFINE_IDR(i2c_adapter_idr);
|
|||
|
||||
static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver);
|
||||
|
||||
static struct static_key i2c_trace_msg = STATIC_KEY_INIT_FALSE;
|
||||
static DEFINE_STATIC_KEY_FALSE(i2c_trace_msg_key);
|
||||
static bool is_registered;
|
||||
|
||||
int i2c_transfer_trace_reg(void)
|
||||
{
|
||||
static_key_slow_inc(&i2c_trace_msg);
|
||||
static_branch_inc(&i2c_trace_msg_key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void i2c_transfer_trace_unreg(void)
|
||||
{
|
||||
static_key_slow_dec(&i2c_trace_msg);
|
||||
static_branch_dec(&i2c_trace_msg_key);
|
||||
}
|
||||
|
||||
const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
|
||||
|
@ -124,6 +126,10 @@ static int i2c_device_uevent(struct device *dev, struct kobj_uevent_env *env)
|
|||
struct i2c_client *client = to_i2c_client(dev);
|
||||
int rc;
|
||||
|
||||
rc = of_device_uevent_modalias(dev, env);
|
||||
if (rc != -ENODEV)
|
||||
return rc;
|
||||
|
||||
rc = acpi_device_uevent_modalias(dev, env);
|
||||
if (rc != -ENODEV)
|
||||
return rc;
|
||||
|
@ -439,6 +445,10 @@ show_modalias(struct device *dev, struct device_attribute *attr, char *buf)
|
|||
struct i2c_client *client = to_i2c_client(dev);
|
||||
int len;
|
||||
|
||||
len = of_device_modalias(dev, buf, PAGE_SIZE);
|
||||
if (len != -ENODEV)
|
||||
return len;
|
||||
|
||||
len = acpi_device_modalias(dev, buf, PAGE_SIZE -1);
|
||||
if (len != -ENODEV)
|
||||
return len;
|
||||
|
@ -507,7 +517,7 @@ static unsigned short i2c_encode_flags_to_addr(struct i2c_client *client)
|
|||
|
||||
/* This is a permissive address validity check, I2C address map constraints
|
||||
* are purposely not enforced, except for the general call address. */
|
||||
int i2c_check_addr_validity(unsigned addr, unsigned short flags)
|
||||
static int i2c_check_addr_validity(unsigned int addr, unsigned short flags)
|
||||
{
|
||||
if (flags & I2C_CLIENT_TEN) {
|
||||
/* 10-bit address, all values are valid */
|
||||
|
@ -1838,11 +1848,12 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
|||
if (adap->quirks && i2c_check_for_quirks(adap, msgs, num))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* i2c_trace_msg gets enabled when tracepoint i2c_transfer gets
|
||||
/*
|
||||
* i2c_trace_msg_key gets enabled when tracepoint i2c_transfer gets
|
||||
* enabled. This is an efficient way of keeping the for-loop from
|
||||
* being executed when not needed.
|
||||
*/
|
||||
if (static_key_false(&i2c_trace_msg)) {
|
||||
if (static_branch_unlikely(&i2c_trace_msg_key)) {
|
||||
int i;
|
||||
for (i = 0; i < num; i++)
|
||||
if (msgs[i].flags & I2C_M_RD)
|
||||
|
@ -1861,12 +1872,12 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
|||
break;
|
||||
}
|
||||
|
||||
if (static_key_false(&i2c_trace_msg)) {
|
||||
if (static_branch_unlikely(&i2c_trace_msg_key)) {
|
||||
int i;
|
||||
for (i = 0; i < ret; i++)
|
||||
if (msgs[i].flags & I2C_M_RD)
|
||||
trace_i2c_reply(adap, &msgs[i], i);
|
||||
trace_i2c_result(adap, i, ret);
|
||||
trace_i2c_result(adap, num, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -1968,6 +1979,37 @@ int i2c_transfer_buffer_flags(const struct i2c_client *client, char *buf,
|
|||
}
|
||||
EXPORT_SYMBOL(i2c_transfer_buffer_flags);
|
||||
|
||||
/**
|
||||
* i2c_get_device_id - get manufacturer, part id and die revision of a device
|
||||
* @client: The device to query
|
||||
* @id: The queried information
|
||||
*
|
||||
* Returns negative errno on error, zero on success.
|
||||
*/
|
||||
int i2c_get_device_id(const struct i2c_client *client,
|
||||
struct i2c_device_identity *id)
|
||||
{
|
||||
struct i2c_adapter *adap = client->adapter;
|
||||
union i2c_smbus_data raw_id;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_READ_I2C_BLOCK))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
raw_id.block[0] = 3;
|
||||
ret = i2c_smbus_xfer(adap, I2C_ADDR_DEVICE_ID, 0,
|
||||
I2C_SMBUS_READ, client->addr << 1,
|
||||
I2C_SMBUS_I2C_BLOCK_DATA, &raw_id);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
id->manufacturer_id = (raw_id.block[1] << 4) | (raw_id.block[2] >> 4);
|
||||
id->part_id = ((raw_id.block[2] & 0xf) << 5) | (raw_id.block[3] >> 3);
|
||||
id->die_revision = raw_id.block[3] & 0x7;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i2c_get_device_id);
|
||||
|
||||
/* ----------------------------------------------------
|
||||
* the i2c address scanning function
|
||||
* Will not work for 10-bit addresses!
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* Copyright (C) 2008 Jochen Friedrich <jochen@scram.de>
|
||||
* based on a previous patch from Jon Smirl <jonsmirl@gmail.com>
|
||||
*
|
||||
* Copyright (C) 2013 Wolfram Sang <wsa@the-dreams.de>
|
||||
* Copyright (C) 2013, 2018 Wolfram Sang <wsa@the-dreams.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
|
@ -25,12 +25,11 @@
|
|||
static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
|
||||
struct device_node *node)
|
||||
{
|
||||
struct i2c_client *result;
|
||||
struct i2c_client *client;
|
||||
struct i2c_board_info info = {};
|
||||
struct dev_archdata dev_ad = {};
|
||||
const __be32 *addr_be;
|
||||
u32 addr;
|
||||
int len;
|
||||
int ret;
|
||||
|
||||
dev_dbg(&adap->dev, "of_i2c: register %pOF\n", node);
|
||||
|
||||
|
@ -40,13 +39,12 @@ static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
|
|||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
addr_be = of_get_property(node, "reg", &len);
|
||||
if (!addr_be || (len < sizeof(*addr_be))) {
|
||||
ret = of_property_read_u32(node, "reg", &addr);
|
||||
if (ret) {
|
||||
dev_err(&adap->dev, "of_i2c: invalid reg on %pOF\n", node);
|
||||
return ERR_PTR(-EINVAL);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
addr = be32_to_cpup(addr_be);
|
||||
if (addr & I2C_TEN_BIT_ADDRESS) {
|
||||
addr &= ~I2C_TEN_BIT_ADDRESS;
|
||||
info.flags |= I2C_CLIENT_TEN;
|
||||
|
@ -57,15 +55,9 @@ static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
|
|||
info.flags |= I2C_CLIENT_SLAVE;
|
||||
}
|
||||
|
||||
if (i2c_check_addr_validity(addr, info.flags)) {
|
||||
dev_err(&adap->dev, "of_i2c: invalid addr=%x on %pOF\n",
|
||||
addr, node);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
info.addr = addr;
|
||||
info.of_node = of_node_get(node);
|
||||
info.archdata = &dev_ad;
|
||||
info.of_node = of_node_get(node);
|
||||
|
||||
if (of_property_read_bool(node, "host-notify"))
|
||||
info.flags |= I2C_CLIENT_HOST_NOTIFY;
|
||||
|
@ -73,13 +65,13 @@ static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
|
|||
if (of_get_property(node, "wakeup-source", NULL))
|
||||
info.flags |= I2C_CLIENT_WAKE;
|
||||
|
||||
result = i2c_new_device(adap, &info);
|
||||
if (result == NULL) {
|
||||
client = i2c_new_device(adap, &info);
|
||||
if (!client) {
|
||||
dev_err(&adap->dev, "of_i2c: Failure registering %pOF\n", node);
|
||||
of_node_put(node);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
return result;
|
||||
return client;
|
||||
}
|
||||
|
||||
void of_i2c_register_devices(struct i2c_adapter *adap)
|
||||
|
@ -103,7 +95,7 @@ void of_i2c_register_devices(struct i2c_adapter *adap)
|
|||
|
||||
client = of_i2c_register_device(adap, node);
|
||||
if (IS_ERR(client)) {
|
||||
dev_warn(&adap->dev,
|
||||
dev_err(&adap->dev,
|
||||
"Failed to create I2C device for %pOF\n",
|
||||
node);
|
||||
of_node_clear_flag(node, OF_POPULATED);
|
||||
|
|
|
@ -308,17 +308,21 @@ static void i2c_smbus_try_get_dmabuf(struct i2c_msg *msg, u8 init_val)
|
|||
msg->buf[0] = init_val;
|
||||
}
|
||||
|
||||
/* Simulate a SMBus command using the i2c protocol
|
||||
No checking of parameters is done! */
|
||||
/*
|
||||
* Simulate a SMBus command using the I2C protocol.
|
||||
* No checking of parameters is done!
|
||||
*/
|
||||
static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
|
||||
unsigned short flags,
|
||||
char read_write, u8 command, int size,
|
||||
union i2c_smbus_data *data)
|
||||
{
|
||||
/* So we need to generate a series of msgs. In the case of writing, we
|
||||
need to use only one message; when reading, we need two. We initialize
|
||||
most things with sane defaults, to keep the code below somewhat
|
||||
simpler. */
|
||||
/*
|
||||
* So we need to generate a series of msgs. In the case of writing, we
|
||||
* need to use only one message; when reading, we need two. We
|
||||
* initialize most things with sane defaults, to keep the code below
|
||||
* somewhat simpler.
|
||||
*/
|
||||
unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3];
|
||||
unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];
|
||||
int num = read_write == I2C_SMBUS_READ ? 2 : 1;
|
||||
|
|
|
@ -27,7 +27,6 @@ extern struct rw_semaphore __i2c_board_lock;
|
|||
extern struct list_head __i2c_board_list;
|
||||
extern int __i2c_first_dynamic_bus_num;
|
||||
|
||||
int i2c_check_addr_validity(unsigned addr, unsigned short flags);
|
||||
int i2c_check_7bit_addr_validity_strict(unsigned short addr);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
|
|
@ -77,6 +77,7 @@ struct chip_desc {
|
|||
pca954x_ismux = 0,
|
||||
pca954x_isswi
|
||||
} muxtype;
|
||||
struct i2c_device_identity id;
|
||||
};
|
||||
|
||||
struct pca954x {
|
||||
|
@ -97,59 +98,83 @@ static const struct chip_desc chips[] = {
|
|||
.nchans = 2,
|
||||
.enable = 0x4,
|
||||
.muxtype = pca954x_ismux,
|
||||
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
|
||||
},
|
||||
[pca_9542] = {
|
||||
.nchans = 2,
|
||||
.enable = 0x4,
|
||||
.has_irq = 1,
|
||||
.muxtype = pca954x_ismux,
|
||||
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
|
||||
},
|
||||
[pca_9543] = {
|
||||
.nchans = 2,
|
||||
.has_irq = 1,
|
||||
.muxtype = pca954x_isswi,
|
||||
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
|
||||
},
|
||||
[pca_9544] = {
|
||||
.nchans = 4,
|
||||
.enable = 0x4,
|
||||
.has_irq = 1,
|
||||
.muxtype = pca954x_ismux,
|
||||
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
|
||||
},
|
||||
[pca_9545] = {
|
||||
.nchans = 4,
|
||||
.has_irq = 1,
|
||||
.muxtype = pca954x_isswi,
|
||||
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
|
||||
},
|
||||
[pca_9546] = {
|
||||
.nchans = 4,
|
||||
.muxtype = pca954x_isswi,
|
||||
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
|
||||
},
|
||||
[pca_9547] = {
|
||||
.nchans = 8,
|
||||
.enable = 0x8,
|
||||
.muxtype = pca954x_ismux,
|
||||
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
|
||||
},
|
||||
[pca_9548] = {
|
||||
.nchans = 8,
|
||||
.muxtype = pca954x_isswi,
|
||||
.id = { .manufacturer_id = I2C_DEVICE_ID_NONE },
|
||||
},
|
||||
[pca_9846] = {
|
||||
.nchans = 4,
|
||||
.muxtype = pca954x_isswi,
|
||||
.id = {
|
||||
.manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS,
|
||||
.part_id = 0x10b,
|
||||
},
|
||||
},
|
||||
[pca_9847] = {
|
||||
.nchans = 8,
|
||||
.enable = 0x8,
|
||||
.muxtype = pca954x_ismux,
|
||||
.id = {
|
||||
.manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS,
|
||||
.part_id = 0x108,
|
||||
},
|
||||
},
|
||||
[pca_9848] = {
|
||||
.nchans = 8,
|
||||
.muxtype = pca954x_isswi,
|
||||
.id = {
|
||||
.manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS,
|
||||
.part_id = 0x10a,
|
||||
},
|
||||
},
|
||||
[pca_9849] = {
|
||||
.nchans = 4,
|
||||
.enable = 0x4,
|
||||
.muxtype = pca954x_ismux,
|
||||
.id = {
|
||||
.manufacturer_id = I2C_DEVICE_ID_NXP_SEMICONDUCTORS,
|
||||
.part_id = 0x109,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -369,6 +394,30 @@ static int pca954x_probe(struct i2c_client *client,
|
|||
if (IS_ERR(gpio))
|
||||
return PTR_ERR(gpio);
|
||||
|
||||
match = of_match_device(of_match_ptr(pca954x_of_match), &client->dev);
|
||||
if (match)
|
||||
data->chip = of_device_get_match_data(&client->dev);
|
||||
else
|
||||
data->chip = &chips[id->driver_data];
|
||||
|
||||
if (data->chip->id.manufacturer_id != I2C_DEVICE_ID_NONE) {
|
||||
struct i2c_device_identity id;
|
||||
|
||||
ret = i2c_get_device_id(client, &id);
|
||||
if (ret && ret != -EOPNOTSUPP)
|
||||
return ret;
|
||||
|
||||
if (!ret &&
|
||||
(id.manufacturer_id != data->chip->id.manufacturer_id ||
|
||||
id.part_id != data->chip->id.part_id)) {
|
||||
dev_warn(&client->dev,
|
||||
"unexpected device id %03x-%03x-%x\n",
|
||||
id.manufacturer_id, id.part_id,
|
||||
id.die_revision);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
/* Write the mux register at addr to verify
|
||||
* that the mux is in fact present. This also
|
||||
* initializes the mux to disconnected state.
|
||||
|
@ -378,12 +427,6 @@ static int pca954x_probe(struct i2c_client *client,
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
match = of_match_device(of_match_ptr(pca954x_of_match), &client->dev);
|
||||
if (match)
|
||||
data->chip = of_device_get_match_data(&client->dev);
|
||||
else
|
||||
data->chip = &chips[id->driver_data];
|
||||
|
||||
data->last_chan = 0; /* force the first selection */
|
||||
|
||||
idle_disconnect_dt = of_node &&
|
||||
|
|
|
@ -3,9 +3,6 @@
|
|||
#define I2C_PCA9564_PLATFORM_H
|
||||
|
||||
struct i2c_pca9564_pf_platform_data {
|
||||
int gpio; /* pin to reset chip. driver will work when
|
||||
* not supplied (negative value), but it
|
||||
* cannot exit some error conditions then */
|
||||
int i2c_clock_speed; /* values are defined in linux/i2c-algo-pca.h */
|
||||
int timeout; /* timeout in jiffies */
|
||||
};
|
||||
|
|
|
@ -47,6 +47,7 @@ struct i2c_algorithm;
|
|||
struct i2c_adapter;
|
||||
struct i2c_client;
|
||||
struct i2c_driver;
|
||||
struct i2c_device_identity;
|
||||
union i2c_smbus_data;
|
||||
struct i2c_board_info;
|
||||
enum i2c_slave_event;
|
||||
|
@ -186,8 +187,37 @@ extern s32 i2c_smbus_write_i2c_block_data(const struct i2c_client *client,
|
|||
extern s32
|
||||
i2c_smbus_read_i2c_block_data_or_emulated(const struct i2c_client *client,
|
||||
u8 command, u8 length, u8 *values);
|
||||
int i2c_get_device_id(const struct i2c_client *client,
|
||||
struct i2c_device_identity *id);
|
||||
#endif /* I2C */
|
||||
|
||||
/**
|
||||
* struct i2c_device_identity - i2c client device identification
|
||||
* @manufacturer_id: 0 - 4095, database maintained by NXP
|
||||
* @part_id: 0 - 511, according to manufacturer
|
||||
* @die_revision: 0 - 7, according to manufacturer
|
||||
*/
|
||||
struct i2c_device_identity {
|
||||
u16 manufacturer_id;
|
||||
#define I2C_DEVICE_ID_NXP_SEMICONDUCTORS 0
|
||||
#define I2C_DEVICE_ID_NXP_SEMICONDUCTORS_1 1
|
||||
#define I2C_DEVICE_ID_NXP_SEMICONDUCTORS_2 2
|
||||
#define I2C_DEVICE_ID_NXP_SEMICONDUCTORS_3 3
|
||||
#define I2C_DEVICE_ID_RAMTRON_INTERNATIONAL 4
|
||||
#define I2C_DEVICE_ID_ANALOG_DEVICES 5
|
||||
#define I2C_DEVICE_ID_STMICROELECTRONICS 6
|
||||
#define I2C_DEVICE_ID_ON_SEMICONDUCTOR 7
|
||||
#define I2C_DEVICE_ID_SPRINTEK_CORPORATION 8
|
||||
#define I2C_DEVICE_ID_ESPROS_PHOTONICS_AG 9
|
||||
#define I2C_DEVICE_ID_FUJITSU_SEMICONDUCTOR 10
|
||||
#define I2C_DEVICE_ID_FLIR 11
|
||||
#define I2C_DEVICE_ID_O2MICRO 12
|
||||
#define I2C_DEVICE_ID_ATMEL 13
|
||||
#define I2C_DEVICE_ID_NONE 0xffff
|
||||
u16 part_id;
|
||||
u8 die_revision;
|
||||
};
|
||||
|
||||
enum i2c_alert_protocol {
|
||||
I2C_PROTOCOL_SMBUS_ALERT,
|
||||
I2C_PROTOCOL_SMBUS_HOST_NOTIFY,
|
||||
|
|
Loading…
Reference in New Issue