Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

i2c: add Phytium i2c driver for both server and desktop platform #123

Merged
merged 4 commits into from
May 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions Documentation/devicetree/bindings/i2c/phytium,i2c.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/i2c/snps,designware-i2c.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

title: Phytium I2C/SMBus Controller

maintainers:
- Chen Baozi <[email protected]>

allOf:
- $ref: /schemas/i2c/i2c-controller.yaml#

properties:
compatible:
const: phytium,i2c

reg:
minItems: 1
items:
- description: Offset and length of the memory mapped registers

interrupts:
maxItems: 1

interrupt-names:
const: smbus_alert
description: should be "smbus_alert" if SMBus alert interrupt is supported

clocks:
minItems: 1
items:
- description: I2C controller reference clock source

unevaluatedProperties: false

required:
- compatible
- reg
- interrupts

examples:
- |
i2c0: i2c@28011000 {
compatible = "phytium,i2c";
reg = <0x0 0x28011000 0x0 0x1000>;
interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "smbus_alert";
clocks = <&sysclk_48mhz>;
};
6 changes: 6 additions & 0 deletions drivers/acpi/acpi_apd.c
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,11 @@ static const struct apd_device_desc hip08_lite_i2c_desc = {
.fixed_clk_rate = 125000000,
};

static const struct apd_device_desc phytium_i2c_desc = {
.setup = acpi_apd_setup,
.fixed_clk_rate = 200000000,
};

static const struct apd_device_desc thunderx2_i2c_desc = {
.setup = acpi_apd_setup,
.fixed_clk_rate = 125000000,
Expand Down Expand Up @@ -246,6 +251,7 @@ static const struct acpi_device_id acpi_apd_device_ids[] = {
{ "HISI02A3", APD_ADDR(hip08_lite_i2c_desc) },
{ "HISI0173", APD_ADDR(hip08_spi_desc) },
{ "NXP0001", APD_ADDR(nxp_i2c_desc) },
{ "PHYT0003", APD_ADDR(phytium_i2c_desc) },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

根据飞腾ACPI规范文档v1.2版本 p13页所说和一些旧代码的内容,
这里下面还需要添加FTI20001?
As Phytium ACPI Description Specification document v1.2 p13
Device HID said for old v1.0 Spec,FTI20001 is need?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

目前飞腾向主流开源社区推送的版本已经剔除了CID属性,如果还在使用acpi v1.0的旧规范的话建议是升级固件了,有很多属性acpi v1.0都不支持的。

#endif
{ }
};
Expand Down
28 changes: 28 additions & 0 deletions drivers/i2c/busses/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -938,6 +938,34 @@ config I2C_PCA_PLATFORM
This driver can also be built as a module. If so, the module
will be called i2c-pca-platform.

config I2C_PHYTIUM_CORE
tristate

config I2C_PHYTIUM_PCI
tristate "Phytium I2C PCI"
depends on PCI && ARCH_PHYTIUM
select I2C_PHYTIUM_CORE
select I2C_SMBUS
help
If you say yes to this option, support will be included for the
Phytium I2C adapter. Only master mode is supported.

This driver can also be built as a module. If so, the module
will be called i2c-phytium-pci.

config I2C_PHYTIUM_PLATFORM
tristate "Phytium I2C Platform"
depends on (ACPI && COMMON_CLK) || !ACPI
select I2C_SLAVE
select I2C_PHYTIUM_CORE
select I2C_SMBUS
help
If you say yes to this option, support will be included for the
Phytium I2C adapter. Only master mode is supported.

This driver can also be built as a module. If so, the module
will be called i2c-phytium-platform.

config I2C_PNX
tristate "I2C bus support for Philips PNX and NXP LPC targets"
depends on ARCH_LPC32XX || COMPILE_TEST
Expand Down
4 changes: 4 additions & 0 deletions drivers/i2c/busses/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ obj-$(CONFIG_I2C_OWL) += i2c-owl.o
obj-$(CONFIG_I2C_PASEMI) += i2c-pasemi-core.o i2c-pasemi-pci.o
obj-$(CONFIG_I2C_APPLE) += i2c-pasemi-core.o i2c-pasemi-platform.o
obj-$(CONFIG_I2C_PCA_PLATFORM) += i2c-pca-platform.o
obj-$(CONFIG_I2C_PHYTIUM_CORE) += i2c-phytium-core.o
i2c-phytium-core-objs := i2c-phytium-common.o i2c-phytium-master.o i2c-phytium-slave.o
obj-$(CONFIG_I2C_PHYTIUM_PCI) += i2c-phytium-pci.o
obj-$(CONFIG_I2C_PHYTIUM_PLATFORM) += i2c-phytium-platform.o
obj-$(CONFIG_I2C_PNX) += i2c-pnx.o
obj-$(CONFIG_I2C_PXA) += i2c-pxa.o
obj-$(CONFIG_I2C_PXA_PCI) += i2c-pxa-pci.o
Expand Down
1 change: 1 addition & 0 deletions drivers/i2c/busses/i2c-designware-platdrv.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ static const struct acpi_device_id dw_i2c_acpi_match[] = {
{ "HISI02A2", 0 },
{ "HISI02A3", 0 },
{ "HYGO0010", ACCESS_INTR_MASK },
{ "PHYT0003", 0 },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

同上

{ }
};
MODULE_DEVICE_TABLE(acpi, dw_i2c_acpi_match);
Expand Down
206 changes: 206 additions & 0 deletions drivers/i2c/busses/i2c-phytium-common.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Phytium I2C adapter driver.
*
* Derived from Synopysys I2C driver.
* Copyright (C) 2006 Texas Instruments.
* Copyright (C) 2007 MontaVista Software Inc.
* Copyright (C) 2009 Provigent Ltd.
*
* Copyright (C) 2021-2023, Phytium Technology Co., Ltd.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/swab.h>

#include "i2c-phytium-core.h"

static char *abort_sources[] = {
[ABRT_7B_ADDR_NOACK] =
"slave address not acknowledged (7bit mode)",
[ABRT_10ADDR1_NOACK] =
"first address byte not acknowledged (10bit mode)",
[ABRT_10ADDR2_NOACK] =
"second address byte not acknowledged (10bit mode)",
[ABRT_TXDATA_NOACK] =
"data not acknowledged",
[ABRT_GCALL_NOACK] =
"no acknowledgment for a general call",
[ABRT_GCALL_READ] =
"read after general call",
[ABRT_SBYTE_ACKDET] =
"start byte acknowledged",
[ABRT_SBYTE_NORSTRT] =
"trying to send start byte when restart is disabled",
[ABRT_10B_RD_NORSTRT] =
"trying to read when restart is disabled (10bit mode)",
[ABRT_MASTER_DIS] =
"trying to use disabled adapter",
[ARB_LOST] =
"lost arbitration",
[ABRT_SLAVE_FLUSH_TXFIFO] =
"read command so flush old data in the TX FIFO",
[ABRT_SLAVE_ARBLOST] =
"slave lost the bus while transmitting data to a remote master",
[ABRT_SLAVE_RD_INTX] =
"incorrect slave-transmitter mode configuration",
};

u32 phytium_readl(struct phytium_i2c_dev *dev, int offset)
{
return readl_relaxed(dev->base + offset);
}

void phytium_writel(struct phytium_i2c_dev *dev, u32 b, int offset)
{
writel_relaxed(b, dev->base + offset);
}

u32 i2c_phytium_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset)
{
if (cond)
return (ic_clk * tSYMBOL + 500000) / 1000000 - 8 + offset;
else
return (ic_clk * (tSYMBOL + tf) + 500000) / 1000000 - 3 + offset;
}

u32 i2c_phytium_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset)
{
return ((ic_clk * (tLOW + tf) + 500000) / 1000000) - 1 + offset;
}

int i2c_phytium_set_sda_hold(struct phytium_i2c_dev *dev)
{
if (!dev->sda_hold_time) {
/* Keep previous hold time setting if no one set it */
dev->sda_hold_time = phytium_readl(dev, IC_SDA_HOLD);
}

if (!(dev->sda_hold_time & IC_SDA_HOLD_RX_MASK))
dev->sda_hold_time |= 1 << IC_SDA_HOLD_RX_SHIFT;

dev_dbg(dev->dev, "SDA Hold Time TX:RX = %d:%d\n",
dev->sda_hold_time & ~(u32)IC_SDA_HOLD_RX_MASK,
dev->sda_hold_time >> IC_SDA_HOLD_RX_SHIFT);

return 0;
}

void __i2c_phytium_disable(struct phytium_i2c_dev *dev)
{
int timeout = 100;

do {
__i2c_phytium_disable_nowait(dev);
if ((phytium_readl(dev, IC_ENABLE_STATUS) & 1) == 0)
return;

/*
* Wait 10 times the signaling period of the highest I2C
* transfer supported by the driver (for 400KHz this is
* 25us).
*/
usleep_range(25, 250);
} while (timeout--);

dev_warn(dev->dev, "timeout in disabling adapter\n");
}

unsigned long i2c_phytium_clk_rate(struct phytium_i2c_dev *dev)
{
if (WARN_ON_ONCE(!dev->get_clk_rate_khz))
return 0;
return dev->get_clk_rate_khz(dev);
}

int i2c_phytium_prepare_clk(struct phytium_i2c_dev *dev, bool prepare)
{
if (IS_ERR(dev->clk))
return PTR_ERR(dev->clk);

if (prepare)
return clk_prepare_enable(dev->clk);

clk_disable_unprepare(dev->clk);
return 0;
}
EXPORT_SYMBOL_GPL(i2c_phytium_prepare_clk);

int i2c_phytium_wait_bus_not_busy(struct phytium_i2c_dev *dev)
{
int timeout = 20; /* 20 ms */

while (phytium_readl(dev, IC_STATUS) & IC_STATUS_ACTIVITY) {
if (timeout <= 0) {
dev_warn(dev->dev, "timeout waiting for bus ready\n");
i2c_recover_bus(&dev->adapter);

if (phytium_readl(dev, IC_STATUS) & IC_STATUS_ACTIVITY)
return -ETIMEDOUT;
return 0;
}
timeout--;
usleep_range(1000, 1100);
}

return 0;
}

int i2c_phytium_handle_tx_abort(struct phytium_i2c_dev *dev)
{
unsigned long abort_source = dev->abort_source;
int i;

if (abort_source & IC_TX_ABRT_NOACK) {
for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources))
dev_dbg(dev->dev,
"%s: %s\n", __func__, abort_sources[i]);
return -EREMOTEIO;
}

for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources))
dev_err(dev->dev, "%s: %s\n", __func__, abort_sources[i]);

if (abort_source & IC_TX_ARB_LOST)
return -EAGAIN;
else if (abort_source & IC_TX_ABRT_GCALL_READ)
return -EINVAL;
else
return -EIO;

return 0;
}

u32 i2c_phytium_func(struct i2c_adapter *adapter)
{
struct phytium_i2c_dev *dev = i2c_get_adapdata(adapter);

return dev->functionality;
}

void i2c_phytium_disable(struct phytium_i2c_dev *dev)
{
/* Disable controller */
__i2c_phytium_disable(dev);

/* Disable all interrupts */
phytium_writel(dev, 0, IC_INTR_MASK);
phytium_readl(dev, IC_CLR_INTR);
}

void i2c_phytium_disable_int(struct phytium_i2c_dev *dev)
{
phytium_writel(dev, 0, IC_INTR_MASK);
}

MODULE_AUTHOR("Cheng Quan <[email protected]>");
MODULE_DESCRIPTION("Phytium I2C bus adapter core");
MODULE_LICENSE("GPL");
Loading