mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-09 12:17:12 +09:00
Merge commit '3a8b94c5be2ca4a2a9dd5916074e2f466ebcc57f'
* commit '3a8b94c5be2ca4a2a9dd5916074e2f466ebcc57f': arm64: dts: rockchip: rv1126b: Remove dma for i2c0, i2c4 and i2c5 dt-bindings: i2c: rockchip: add rockchip,rv1126b-i2c i2c: rk3x: Add dma feature arm64: dts: rockchip: rv1126b-evb: Change clock rates to 24M for fephy ethernet: stmmac: dwmac-rk: Correct clock input/output sel for RV1126B net: phy: rockchip-fephy: Add param to access group registers net: phy: rockchip-fephy: Fix for the correct names net: phy: rockchip-fephy: Add 24M clock rate setting net: phy: rockchip-fephy: Change off-energy level0 threshold between link up/down Change-Id: I93555893bf14d5c358c0e583917daeae6295b483
This commit is contained in:
@@ -21,6 +21,7 @@ properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: rockchip,rv1108-i2c
|
||||
- const: rockchip,rv1126b-i2c
|
||||
- const: rockchip,rk3066-i2c
|
||||
- const: rockchip,rk3188-i2c
|
||||
- const: rockchip,rk3228-i2c
|
||||
|
||||
@@ -325,7 +325,7 @@
|
||||
compatible = "ethernet-phy-id0680.8101", "ethernet-phy-ieee802.3-c22";
|
||||
reg = <2>;
|
||||
clocks = <&cru CLK_MACPHY>;
|
||||
clock-frequency = <50000000>;
|
||||
clock-frequency = <24000000>;
|
||||
resets = <&cru SRST_RESETN_MACPHY>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&fephym1_pins>;
|
||||
|
||||
@@ -320,7 +320,7 @@
|
||||
compatible = "ethernet-phy-id0680.8101", "ethernet-phy-ieee802.3-c22";
|
||||
reg = <2>;
|
||||
clocks = <&cru CLK_MACPHY>;
|
||||
clock-frequency = <50000000>;
|
||||
clock-frequency = <24000000>;
|
||||
resets = <&cru SRST_RESETN_MACPHY>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&fephym2_pins>;
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
compatible = "ethernet-phy-id0680.8101", "ethernet-phy-ieee802.3-c22";
|
||||
reg = <2>;
|
||||
clocks = <&cru CLK_MACPHY>;
|
||||
clock-frequency = <50000000>;
|
||||
clock-frequency = <24000000>;
|
||||
resets = <&cru SRST_RESETN_MACPHY>;
|
||||
phy-is-integrated;
|
||||
};
|
||||
|
||||
@@ -1576,7 +1576,7 @@
|
||||
};
|
||||
|
||||
i2c2: i2c@20800000 {
|
||||
compatible = "rockchip,rv1126b-i2c", "rockchip,rk3399-i2c";
|
||||
compatible = "rockchip,rv1126b-i2c";
|
||||
reg = <0x20800000 0x1000>;
|
||||
interrupts = <GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#address-cells = <1>;
|
||||
@@ -2273,22 +2273,20 @@
|
||||
};
|
||||
|
||||
i2c0: i2c@21100000 {
|
||||
compatible = "rockchip,rv1126b-i2c", "rockchip,rk3399-i2c";
|
||||
compatible = "rockchip,rv1126b-i2c";
|
||||
reg = <0x21100000 0x1000>;
|
||||
interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&cru CLK_I2C0>, <&cru PCLK_I2C0>;
|
||||
clock-names = "i2c", "pclk";
|
||||
dmas = <&dmac 29>, <&dmac 28>;
|
||||
dma-names = "tx", "rx";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&i2c0m0_pins>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
i2c1: i2c@21110000 {
|
||||
compatible = "rockchip,rv1126b-i2c", "rockchip,rk3399-i2c";
|
||||
compatible = "rockchip,rv1126b-i2c";
|
||||
reg = <0x21110000 0x1000>;
|
||||
interrupts = <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#address-cells = <1>;
|
||||
@@ -2303,7 +2301,7 @@
|
||||
};
|
||||
|
||||
i2c3: i2c@21120000 {
|
||||
compatible = "rockchip,rv1126b-i2c", "rockchip,rk3399-i2c";
|
||||
compatible = "rockchip,rv1126b-i2c";
|
||||
reg = <0x21120000 0x1000>;
|
||||
interrupts = <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#address-cells = <1>;
|
||||
@@ -2318,30 +2316,26 @@
|
||||
};
|
||||
|
||||
i2c4: i2c@21130000 {
|
||||
compatible = "rockchip,rv1126b-i2c", "rockchip,rk3399-i2c";
|
||||
compatible = "rockchip,rv1126b-i2c";
|
||||
reg = <0x21130000 0x1000>;
|
||||
interrupts = <GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&cru CLK_I2C4>, <&cru PCLK_I2C4>;
|
||||
clock-names = "i2c", "pclk";
|
||||
dmas = <&dmac 37>, <&dmac 36>;
|
||||
dma-names = "tx", "rx";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&i2c4m0_pins>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
i2c5: i2c@21140000 {
|
||||
compatible = "rockchip,rv1126b-i2c", "rockchip,rk3399-i2c";
|
||||
compatible = "rockchip,rv1126b-i2c";
|
||||
reg = <0x21140000 0x1000>;
|
||||
interrupts = <GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
clocks = <&cru CLK_I2C5>, <&cru PCLK_I2C5>;
|
||||
clock-names = "i2c", "pclk";
|
||||
dmas = <&dmac 39>, <&dmac 38>;
|
||||
dma-names = "tx", "rx";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&i2c5m0_pins>;
|
||||
status = "disabled";
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
@@ -41,6 +43,10 @@
|
||||
#define REG_IPD 0x1c /* interrupt pending */
|
||||
#define REG_FCNT 0x20 /* finished count */
|
||||
#define REG_SCL_OE_DB 0x24 /* Slave hold scl debounce */
|
||||
#define REG_DMA_SADDR 0x80 /* DMA Addr register for TX */
|
||||
#define REG_DMA_TXDATA 0x88 /* DMA TXDATA register */
|
||||
#define REG_DMA_RXDATA 0x8c /* DMA RXDATA register */
|
||||
#define REG_DMA_CONF 0x90 /* DMA conf register */
|
||||
#define REG_CON1 0x228 /* control register1 */
|
||||
|
||||
/* Data buffer offsets */
|
||||
@@ -62,6 +68,7 @@ enum {
|
||||
#define REG_CON_STOP BIT(4)
|
||||
#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_CLEAN_DMA BIT(7) /* 1: clean DMA conf */
|
||||
|
||||
#define REG_CON_TUNING_MASK GENMASK_ULL(15, 8)
|
||||
|
||||
@@ -90,7 +97,7 @@ enum {
|
||||
#define REG_INT_STOP BIT(5) /* STOP condition generated */
|
||||
#define REG_INT_NAKRCV BIT(6) /* NACK received */
|
||||
#define REG_INT_SLV_HDSCL BIT(7) /* slave hold scl */
|
||||
#define REG_INT_ALL 0xff
|
||||
#define REG_INT_ALL 0x3ff
|
||||
|
||||
/* Disable i2c all irqs */
|
||||
#define IEN_ALL_DISABLE 0
|
||||
@@ -104,9 +111,23 @@ enum {
|
||||
#define REG_CON1_TRANSFER_AUTO_STOP BIT(1)
|
||||
#define REG_CON1_NACK_AUTO_STOP BIT(2)
|
||||
|
||||
/* DMA */
|
||||
#define DMA_ENABLE_DMA_TX (BIT(16) | BIT(0))
|
||||
#define DMA_ENABLE_DMA_RX (BIT(16 + 1) | BIT(1))
|
||||
#define DMA_DISABLE_DMA_TX (BIT(16))
|
||||
#define DMA_DISABLE_DMA_RX (BIT(16 + 1))
|
||||
#define DMA_ENABLE_RK_DMA (BIT(16 + 2) | BIT(2))
|
||||
#define DMA_FLUSH_TXRX (BIT(16 + 4) | BIT(4))
|
||||
|
||||
#define DMA_TX_SLAVE_ADDR_OFFSET 0
|
||||
#define DMA_TX_SLAVE_ADDR1_OFFSE 8 /* for 10bit addr */
|
||||
#define DMA_TX_SLAVE_ADDR_VALID BIT(16)
|
||||
#define DMA_TX_SLAVE_ADDR1_VALID BIT(17) /* for 10bit addr */
|
||||
|
||||
/* Constants */
|
||||
#define WAIT_TIMEOUT 200 /* ms */
|
||||
#define DEFAULT_SCL_RATE (100 * 1000) /* Hz */
|
||||
#define WAIT_TIMEOUT 200 /* ms */
|
||||
#define DEFAULT_SCL_RATE (100 * 1000) /* Hz */
|
||||
#define TX_IDLE_WAIT_TIMEOUT 10 /* ms */
|
||||
|
||||
/**
|
||||
* struct i2c_spec_values - I2C specification values for various modes
|
||||
@@ -186,15 +207,22 @@ enum rk3x_i2c_state {
|
||||
STATE_STOP
|
||||
};
|
||||
|
||||
enum rk3x_i2c_xfer_mode {
|
||||
RK_I2C_FIFO,
|
||||
RK_I2C_DMA
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rk3x_i2c_soc_data - SOC-specific data
|
||||
* @grf_offset: offset inside the grf regmap for setting the i2c type
|
||||
* @calc_timings: Callback function for i2c timing information calculated
|
||||
* @dma_control: DMA initialize or release function for some Socs support DMA
|
||||
*/
|
||||
struct rk3x_i2c_soc_data {
|
||||
int grf_offset;
|
||||
int (*calc_timings)(unsigned long, struct i2c_timings *,
|
||||
struct rk3x_i2c_calced_timings *);
|
||||
void (*dma_control)(struct platform_device *pdev, bool init);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -232,6 +260,12 @@ struct rk3x_i2c {
|
||||
struct clk *clk;
|
||||
struct clk *pclk;
|
||||
struct notifier_block clk_rate_nb;
|
||||
phys_addr_t dma_addr_rx;
|
||||
phys_addr_t dma_addr_tx;
|
||||
struct dma_chan *dma_rx;
|
||||
struct dma_chan *dma_tx;
|
||||
struct scatterlist sg;
|
||||
enum dma_data_direction dma_direction;
|
||||
bool autostop_supported;
|
||||
|
||||
struct reset_control *reset;
|
||||
@@ -251,12 +285,15 @@ struct rk3x_i2c {
|
||||
u8 addr;
|
||||
unsigned int mode;
|
||||
bool is_last_msg;
|
||||
void *dma_buf;
|
||||
|
||||
/* I2C state machine */
|
||||
enum rk3x_i2c_state state;
|
||||
unsigned int processed;
|
||||
int error;
|
||||
unsigned int suspended:1;
|
||||
enum rk3x_i2c_xfer_mode xfer_mode;
|
||||
bool stop_after_dma;
|
||||
|
||||
struct notifier_block i2c_restart_nb;
|
||||
bool system_restarting;
|
||||
@@ -342,16 +379,250 @@ out:
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* rk3x_i2c_start - Generate a START condition, which triggers a REG_INT_START interrupt.
|
||||
* @i2c: target controller data
|
||||
*/
|
||||
static void rk3x_i2c_start(struct rk3x_i2c *i2c)
|
||||
static void rk3x_i2c_dma_unmap(struct rk3x_i2c *i2c)
|
||||
{
|
||||
struct dma_chan *chan = i2c->dma_direction == DMA_FROM_DEVICE
|
||||
? i2c->dma_rx : i2c->dma_tx;
|
||||
|
||||
dma_unmap_single(chan->device->dev, sg_dma_address(&i2c->sg),
|
||||
i2c->msg->len, i2c->dma_direction);
|
||||
|
||||
i2c->dma_direction = DMA_NONE;
|
||||
}
|
||||
|
||||
static void rk3x_i2c_cleanup_dma(struct rk3x_i2c *i2c)
|
||||
{
|
||||
if (i2c->dma_direction == DMA_NONE)
|
||||
return;
|
||||
else if (i2c->dma_direction == DMA_FROM_DEVICE)
|
||||
dmaengine_terminate_all(i2c->dma_rx);
|
||||
else if (i2c->dma_direction == DMA_TO_DEVICE)
|
||||
dmaengine_terminate_all(i2c->dma_tx);
|
||||
|
||||
rk3x_i2c_dma_unmap(i2c);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
rk3x_i2c_wait_for_tx_idle(struct rk3x_i2c *i2c)
|
||||
{
|
||||
u32 ipd;
|
||||
|
||||
if (!readl_relaxed_poll_timeout(i2c->regs + REG_IPD, ipd,
|
||||
ipd & REG_INT_STOP, 100,
|
||||
TX_IDLE_WAIT_TIMEOUT * 1000))
|
||||
return true;
|
||||
|
||||
dev_warn(i2c->dev, "i2c controller is in busy state!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
static void rk3x_i2c_complete_dma(struct rk3x_i2c *i2c, unsigned long time_left)
|
||||
{
|
||||
/* if it is no error happen, copy to rx buf */
|
||||
if (!i2c->error && time_left > 0) {
|
||||
/* if it is dma tx, make sure tx is completed */
|
||||
if (!rk3x_i2c_wait_for_tx_idle(i2c))
|
||||
i2c->error = -EBUSY;
|
||||
else
|
||||
i2c->stop_after_dma = true;
|
||||
}
|
||||
|
||||
rk3x_i2c_cleanup_dma(i2c);
|
||||
i2c_put_dma_safe_msg_buf(i2c->dma_buf, i2c->msg,
|
||||
i2c->stop_after_dma);
|
||||
}
|
||||
|
||||
static void rk3x_i2c_handle_dma_xfer_stop(struct rk3x_i2c *i2c)
|
||||
{
|
||||
u32 con = i2c_readl(i2c, REG_CON);
|
||||
|
||||
/* disable start bit */
|
||||
con &= ~REG_CON_START;
|
||||
i2c_writel(i2c, con, REG_CON);
|
||||
|
||||
if (!i2c->error)
|
||||
i2c->state = STATE_IDLE;
|
||||
|
||||
rk3x_i2c_clean_ipd(i2c);
|
||||
i2c->dma_buf = NULL;
|
||||
i2c->msg = NULL;
|
||||
}
|
||||
|
||||
static void rk3x_i2c_dma_irq_callback(void *data)
|
||||
{
|
||||
struct rk3x_i2c *i2c = (struct rk3x_i2c *)data;
|
||||
|
||||
i2c->busy = false;
|
||||
/* signal rk3x_i2c_xfer that we are finished */
|
||||
rk3x_i2c_wake_up(i2c);
|
||||
}
|
||||
|
||||
static int rk3x_i2c_prepate_dma_sg(struct rk3x_i2c *i2c, void *dma_buf)
|
||||
{
|
||||
struct dma_async_tx_descriptor *rxdesc = NULL, *txdesc = NULL;
|
||||
u32 val = DMA_FLUSH_TXRX | DMA_ENABLE_RK_DMA;
|
||||
bool read = i2c->msg->flags & I2C_M_RD;
|
||||
dma_cookie_t cookie;
|
||||
dma_addr_t dma_addr;
|
||||
|
||||
if (i2c->mode == REG_CON_MOD_TX) {
|
||||
val |= DMA_ENABLE_DMA_TX | DMA_DISABLE_DMA_RX;
|
||||
i2c_writel(i2c, val, REG_DMA_CONF);
|
||||
val = ((i2c->addr & 0x7f) << 1) | DMA_TX_SLAVE_ADDR_VALID;
|
||||
i2c_writel(i2c, val, REG_DMA_SADDR);
|
||||
} else {
|
||||
val |= DMA_ENABLE_DMA_RX | DMA_DISABLE_DMA_TX;
|
||||
i2c_writel(i2c, val, REG_DMA_CONF);
|
||||
}
|
||||
|
||||
if (read) {
|
||||
dma_addr = dma_map_single(i2c->dma_rx->device->dev,
|
||||
dma_buf, i2c->msg->len,
|
||||
DMA_FROM_DEVICE);
|
||||
if (dma_mapping_error(i2c->dma_rx->device->dev, dma_addr)) {
|
||||
dev_err(i2c->dev, "dma rx map failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
dma_addr = dma_map_single(i2c->dma_tx->device->dev,
|
||||
dma_buf, i2c->msg->len,
|
||||
DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(i2c->dma_tx->device->dev, dma_addr)) {
|
||||
dev_err(i2c->dev, "dma tx map failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
sg_dma_len(&i2c->sg) = i2c->msg->len;
|
||||
sg_dma_address(&i2c->sg) = dma_addr;
|
||||
|
||||
if (read) {
|
||||
struct dma_slave_config rxconf = {
|
||||
.direction = DMA_DEV_TO_MEM,
|
||||
.src_addr = i2c->dma_addr_rx,
|
||||
.src_addr_width = 4,
|
||||
.src_maxburst = 4,
|
||||
};
|
||||
|
||||
i2c->dma_direction = DMA_FROM_DEVICE;
|
||||
dmaengine_slave_config(i2c->dma_rx, &rxconf);
|
||||
rxdesc = dmaengine_prep_slave_sg(i2c->dma_rx, &i2c->sg,
|
||||
1, DMA_DEV_TO_MEM,
|
||||
DMA_PREP_INTERRUPT);
|
||||
if (!rxdesc) {
|
||||
if (txdesc)
|
||||
dmaengine_terminate_sync(i2c->dma_tx);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rxdesc->callback = rk3x_i2c_dma_irq_callback;
|
||||
rxdesc->callback_param = i2c;
|
||||
|
||||
cookie = dmaengine_submit(rxdesc);
|
||||
if (dma_submit_error(cookie)) {
|
||||
dev_err(i2c->dev, "submitting dma rx failed\n");
|
||||
rk3x_i2c_cleanup_dma(i2c);
|
||||
return -EINVAL;
|
||||
}
|
||||
dma_async_issue_pending(i2c->dma_rx);
|
||||
} else {
|
||||
struct dma_slave_config txconf = {
|
||||
.direction = DMA_MEM_TO_DEV,
|
||||
.dst_addr = i2c->dma_addr_tx,
|
||||
.dst_addr_width = 4,
|
||||
.dst_maxburst = 4,
|
||||
};
|
||||
|
||||
i2c->dma_direction = DMA_TO_DEVICE;
|
||||
dmaengine_slave_config(i2c->dma_tx, &txconf);
|
||||
txdesc = dmaengine_prep_slave_sg(i2c->dma_tx, &i2c->sg,
|
||||
1, DMA_MEM_TO_DEV,
|
||||
DMA_PREP_INTERRUPT);
|
||||
if (!txdesc) {
|
||||
if (rxdesc)
|
||||
dmaengine_terminate_sync(i2c->dma_rx);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
txdesc->callback = rk3x_i2c_dma_irq_callback;
|
||||
txdesc->callback_param = i2c;
|
||||
|
||||
cookie = dmaengine_submit(txdesc);
|
||||
if (dma_submit_error(cookie)) {
|
||||
dev_err(i2c->dev, "submitting dma tx failed\n");
|
||||
rk3x_i2c_cleanup_dma(i2c);
|
||||
return -EINVAL;
|
||||
}
|
||||
dma_async_issue_pending(i2c->dma_tx);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rk3x_i2c_start_dma(struct rk3x_i2c *i2c, void *dma_buf)
|
||||
{
|
||||
u32 con = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK;
|
||||
u32 con1 = 0;
|
||||
|
||||
i2c->xfer_mode = RK_I2C_DMA;
|
||||
/* use dma should enable autostop, it is also the last msg */
|
||||
if (!(i2c->msg->flags & I2C_M_IGNORE_NAK))
|
||||
con1 = REG_CON1_NACK_AUTO_STOP | REG_CON1_AUTO_STOP;
|
||||
|
||||
con1 |= REG_CON1_TRANSFER_AUTO_STOP | REG_CON1_AUTO_STOP;
|
||||
i2c_writel(i2c, con1, REG_CON1);
|
||||
i2c->state = STATE_STOP;
|
||||
|
||||
/* only enable nack irq for dma mode */
|
||||
i2c_writel(i2c, REG_INT_NAKRCV, REG_IEN);
|
||||
|
||||
rk3x_i2c_prepate_dma_sg(i2c, dma_buf);
|
||||
|
||||
/* enable adapter with correct mode, send START condition */
|
||||
con |= REG_CON_EN | REG_CON_MOD(i2c->mode) | REG_CON_START | REG_CON_LASTACK;
|
||||
/* if we want to react to NACK, set ACTACK bit */
|
||||
if (!(i2c->msg->flags & I2C_M_IGNORE_NAK))
|
||||
con |= REG_CON_ACTACK;
|
||||
i2c_writel(i2c, con, REG_CON);
|
||||
|
||||
/* enable transition */
|
||||
if (i2c->mode == REG_CON_MOD_TX)
|
||||
/* Add one byte for device addr */
|
||||
i2c_writel(i2c, i2c->msg->len + 1, REG_MTXCNT);
|
||||
else
|
||||
i2c_writel(i2c, i2c->msg->len, REG_MRXCNT);
|
||||
}
|
||||
|
||||
static u8 *rk3x_i2c_get_dma_safe_msg_buf(struct i2c_msg *msg, unsigned int threshold)
|
||||
{
|
||||
void *buf;
|
||||
|
||||
/* also skip 0-length msgs for bogus thresholds of 0 */
|
||||
if (!threshold)
|
||||
pr_debug("rk3x i2c DMA buffer for addr=0x%02x with length 0 is bogus\n",
|
||||
msg->addr);
|
||||
|
||||
if (msg->len < threshold || msg->len == 0)
|
||||
return NULL;
|
||||
|
||||
pr_debug("rk3x i2c using bounce buffer for addr=0x%02x, len=%d\n",
|
||||
msg->addr, msg->len);
|
||||
|
||||
if (msg->flags & I2C_M_RD)
|
||||
return kzalloc(ALIGN(msg->len, cache_line_size()), GFP_KERNEL);
|
||||
|
||||
buf = kzalloc(ALIGN(msg->len, cache_line_size()), GFP_KERNEL);
|
||||
memcpy(buf, msg->buf, msg->len);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void rk3x_i2c_start_fifo(struct rk3x_i2c *i2c)
|
||||
{
|
||||
u32 val = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK;
|
||||
bool auto_stop = rk3x_i2c_auto_stop(i2c);
|
||||
int length = 0;
|
||||
|
||||
i2c->xfer_mode = RK_I2C_FIFO;
|
||||
/* enable appropriate interrupts */
|
||||
if (i2c->mode == REG_CON_MOD_TX) {
|
||||
if (!auto_stop) {
|
||||
@@ -368,7 +639,7 @@ static void rk3x_i2c_start(struct rk3x_i2c *i2c)
|
||||
}
|
||||
|
||||
/* 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 | REG_CON_CLEAN_DMA;
|
||||
|
||||
/* if we want to react to NACK, set ACTACK bit */
|
||||
if (!(i2c->msg->flags & I2C_M_IGNORE_NAK))
|
||||
@@ -383,6 +654,25 @@ static void rk3x_i2c_start(struct rk3x_i2c *i2c)
|
||||
rk3x_i2c_prepare_read(i2c);
|
||||
}
|
||||
|
||||
/**
|
||||
* rk3x_i2c_start - Generate a START condition, which triggers a REG_INT_START interrupt.
|
||||
* @i2c: target controller data
|
||||
*/
|
||||
static void rk3x_i2c_start(struct rk3x_i2c *i2c, bool polling)
|
||||
{
|
||||
/* DMA or FIFO */
|
||||
if (i2c->dma_tx && i2c->dma_rx && !polling) {
|
||||
if (i2c->msg->flags & I2C_M_RD)
|
||||
i2c->dma_buf = rk3x_i2c_get_dma_safe_msg_buf(i2c->msg, 65);
|
||||
else
|
||||
i2c->dma_buf = rk3x_i2c_get_dma_safe_msg_buf(i2c->msg, 64);
|
||||
|
||||
if (i2c->dma_buf)
|
||||
return rk3x_i2c_start_dma(i2c, i2c->dma_buf);
|
||||
}
|
||||
rk3x_i2c_start_fifo(i2c);
|
||||
}
|
||||
|
||||
/**
|
||||
* rk3x_i2c_stop - Generate a STOP condition, which triggers a REG_INT_STOP interrupt.
|
||||
* @i2c: target controller data
|
||||
@@ -1171,6 +1461,8 @@ static int rk3x_i2c_setup(struct rk3x_i2c *i2c, struct i2c_msg *msgs, int num)
|
||||
i2c->busy = true;
|
||||
i2c->processed = 0;
|
||||
i2c->error = 0;
|
||||
i2c->xfer_mode = RK_I2C_FIFO;
|
||||
i2c->stop_after_dma = false;
|
||||
|
||||
rk3x_i2c_clean_ipd(i2c);
|
||||
if (i2c->autostop_supported)
|
||||
@@ -1261,21 +1553,29 @@ static int rk3x_i2c_xfer_common(struct i2c_adapter *adap,
|
||||
spin_unlock_irqrestore(&i2c->lock, flags);
|
||||
|
||||
if (!polling) {
|
||||
rk3x_i2c_start(i2c);
|
||||
rk3x_i2c_start(i2c, polling);
|
||||
|
||||
timeout = wait_event_timeout(i2c->wait, !i2c->busy,
|
||||
msecs_to_jiffies(xfer_time));
|
||||
} else {
|
||||
disable_irq(i2c->irq);
|
||||
rk3x_i2c_start(i2c);
|
||||
rk3x_i2c_start(i2c, polling);
|
||||
|
||||
timeout = rk3x_i2c_wait_xfer_poll(i2c, xfer_time);
|
||||
|
||||
enable_irq(i2c->irq);
|
||||
}
|
||||
|
||||
/* 'stop_after_dma' tells if DMA xfer was complete */
|
||||
if (i2c->xfer_mode == RK_I2C_DMA)
|
||||
rk3x_i2c_complete_dma(i2c, timeout);
|
||||
|
||||
spin_lock_irqsave(&i2c->lock, flags);
|
||||
|
||||
/* Handles states of the I2C controller itself for DMA xfer mode */
|
||||
if (i2c->xfer_mode == RK_I2C_DMA)
|
||||
rk3x_i2c_handle_dma_xfer_stop(i2c);
|
||||
|
||||
if (timeout == 0) {
|
||||
ipd = i2c_readl(i2c, REG_IPD);
|
||||
dev_err(i2c->dev, "timeout, ipd: 0x%02x, state: %d\n",
|
||||
@@ -1450,6 +1750,38 @@ static u32 rk3x_i2c_func(struct i2c_adapter *adap)
|
||||
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
|
||||
}
|
||||
|
||||
static void rk3x_i2c_dma_control(struct platform_device *pdev, bool init)
|
||||
{
|
||||
struct rk3x_i2c *i2c = platform_get_drvdata(pdev);
|
||||
|
||||
if (init) {
|
||||
struct resource *res;
|
||||
|
||||
i2c->dma_tx = dma_request_chan(&pdev->dev, "tx");
|
||||
if (IS_ERR(i2c->dma_tx)) {
|
||||
dev_dbg(&pdev->dev, "Failed to request TX DMA channel\n");
|
||||
i2c->dma_tx = NULL;
|
||||
}
|
||||
|
||||
i2c->dma_rx = dma_request_chan(&pdev->dev, "rx");
|
||||
if (IS_ERR(i2c->dma_rx)) {
|
||||
dev_dbg(&pdev->dev, "Failed to request RX DMA channel\n");
|
||||
i2c->dma_rx = NULL;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (i2c->dma_tx)
|
||||
i2c->dma_addr_tx = res->start + REG_DMA_TXDATA;
|
||||
if (i2c->dma_rx)
|
||||
i2c->dma_addr_rx = res->start + REG_DMA_RXDATA;
|
||||
} else {
|
||||
if (i2c->dma_rx)
|
||||
dma_release_channel(i2c->dma_rx);
|
||||
if (i2c->dma_tx)
|
||||
dma_release_channel(i2c->dma_tx);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm rk3x_i2c_algorithm = {
|
||||
.master_xfer = rk3x_i2c_xfer,
|
||||
.master_xfer_atomic = rk3x_i2c_xfer_polling,
|
||||
@@ -1471,6 +1803,12 @@ static const struct rk3x_i2c_soc_data rk3066_soc_data = {
|
||||
.calc_timings = rk3x_i2c_v0_calc_timings,
|
||||
};
|
||||
|
||||
static const struct rk3x_i2c_soc_data rv1126b_soc_data = {
|
||||
.grf_offset = -1,
|
||||
.calc_timings = rk3x_i2c_v1_calc_timings,
|
||||
.dma_control = rk3x_i2c_dma_control,
|
||||
};
|
||||
|
||||
static const struct rk3x_i2c_soc_data rk3188_soc_data = {
|
||||
.grf_offset = 0x0a4,
|
||||
.calc_timings = rk3x_i2c_v0_calc_timings,
|
||||
@@ -1500,6 +1838,10 @@ static const struct of_device_id rk3x_i2c_match[] = {
|
||||
.compatible = "rockchip,rv1126-i2c",
|
||||
.data = &rv1126_soc_data
|
||||
},
|
||||
{
|
||||
.compatible = "rockchip,rv1126b-i2c",
|
||||
.data = &rv1126b_soc_data
|
||||
},
|
||||
{
|
||||
.compatible = "rockchip,rk3066-i2c",
|
||||
.data = &rk3066_soc_data
|
||||
@@ -1720,6 +2062,9 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
|
||||
goto err_clk_notifier;
|
||||
}
|
||||
|
||||
if (i2c->soc_data->dma_control)
|
||||
i2c->soc_data->dma_control(pdev, true);
|
||||
|
||||
ret = i2c_add_numbered_adapter(&i2c->adap);
|
||||
if (ret < 0)
|
||||
goto err_register_restart_handler;
|
||||
@@ -1727,6 +2072,8 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
|
||||
err_register_restart_handler:
|
||||
if (i2c->soc_data->dma_control)
|
||||
i2c->soc_data->dma_control(pdev, false);
|
||||
unregister_restart_handler(&i2c->i2c_restart_nb);
|
||||
err_clk_notifier:
|
||||
clk_notifier_unregister(i2c->clk, &i2c->clk_rate_nb);
|
||||
@@ -1743,6 +2090,9 @@ static int rk3x_i2c_remove(struct platform_device *pdev)
|
||||
|
||||
i2c_del_adapter(&i2c->adap);
|
||||
|
||||
if (i2c->soc_data->dma_control)
|
||||
i2c->soc_data->dma_control(pdev, false);
|
||||
|
||||
unregister_restart_handler(&i2c->i2c_restart_nb);
|
||||
clk_notifier_unregister(i2c->clk, &i2c->clk_rate_nb);
|
||||
clk_unprepare(i2c->pclk);
|
||||
|
||||
@@ -2557,8 +2557,8 @@ static const struct rk_gmac_ops rv1126_ops = {
|
||||
#define RV1126B_RK_MACPHY_DISABLE 0
|
||||
#define RV1126B_RK_MACPHY_ENABLE BIT(31)
|
||||
|
||||
#define RV1126B_RK_MACPHY_EXTCLK_SEL_OUTPUT 0
|
||||
#define RV1126B_RK_MACPHY_EXTCLK_SEL_INPUT BIT(8)
|
||||
#define RV1126B_RK_MACPHY_EXTCLK_SEL_INPUT 0
|
||||
#define RV1126B_RK_MACPHY_EXTCLK_SEL_OUTPUT BIT(8)
|
||||
|
||||
#define RV1126B_RK_MACPHY_CLK_24M 0
|
||||
#define RV1126B_RK_MACPHY_CLK_50M BIT(11)
|
||||
@@ -2686,23 +2686,18 @@ static void rv1126b_integrated_phy_power(struct rk_priv_data *priv, bool up)
|
||||
value = RV1126B_RK_MACPHY_CLK_50M;
|
||||
else
|
||||
value = RV1126B_RK_MACPHY_CLK_24M;
|
||||
|
||||
value |= priv->clock_input ? RV1126B_RK_MACPHY_EXTCLK_SEL_INPUT :
|
||||
RV1126B_RK_MACPHY_EXTCLK_SEL_OUTPUT;
|
||||
|
||||
regmap_write(priv->grf, RV1126B_VI_GRF_RK_MACPHY_CON2, value);
|
||||
|
||||
regmap_write(priv->grf, RV1126B_VI_GRF_RK_MACPHY_CON0,
|
||||
RV1126B_RK_MACPHY_PHY_ID | RV1126B_RK_MACPHY_PHY_ADDR);
|
||||
|
||||
value = RV1126B_RK_MACPHY_PHY_REVISION | RV1126B_RK_MACPHY_PHY_MODEL |
|
||||
RV1126B_RK_MACPHY_ENABLE;
|
||||
regmap_write(priv->grf, RV1126B_VI_GRF_RK_MACPHY_CON1, value);
|
||||
usleep_range(200, 220);
|
||||
reset_control_deassert(priv->phy_reset);
|
||||
regmap_write(priv->grf, RV1126B_VI_GRF_RK_MACPHY_CON1,
|
||||
RV1126B_RK_MACPHY_DISABLE);
|
||||
usleep_range(100, 120);
|
||||
regmap_write(priv->grf, RV1126B_VI_GRF_RK_MACPHY_CON1, value);
|
||||
reset_control_deassert(priv->phy_reset);
|
||||
} else {
|
||||
regmap_write(priv->grf, RV1126B_VI_GRF_RK_MACPHY_CON1,
|
||||
RV1126B_RK_MACPHY_DISABLE);
|
||||
|
||||
@@ -13,12 +13,13 @@
|
||||
#include <linux/mii.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/phy.h>
|
||||
|
||||
#define INTERNAL_FEPHY_ID 0x06808101
|
||||
|
||||
#define MII_INTERNAL_CTRL_STATUS 17
|
||||
#define SMI_ADDR_TSTCNTL 20
|
||||
#define SMI_ADDR_CFGCNTL 20
|
||||
#define SMI_ADDR_TSTREAD1 21
|
||||
#define SMI_ADDR_TSTREAD2 22
|
||||
#define SMI_ADDR_TSTWRITE 23
|
||||
@@ -33,62 +34,54 @@
|
||||
#define MII_SPEED_10 BIT(2)
|
||||
#define MII_SPEED_100 BIT(3)
|
||||
|
||||
#define TSTCNTL_WRITE_ADDR 0
|
||||
#define TSTCNTL_READ_ADDR 5
|
||||
#define TSTCNTL_BANK_SEL 11
|
||||
#define TSTCNTL_RD (BIT(15) | BIT(10))
|
||||
#define TSTCNTL_WR (BIT(14) | BIT(10))
|
||||
#define CFGCNTL_WRITE_ADDR 0
|
||||
#define CFGCNTL_READ_ADDR 5
|
||||
#define CFGCNTL_GROUP_SEL 11
|
||||
#define CFGCNTL_RD (BIT(15) | BIT(10))
|
||||
#define CFGCNTL_WR (BIT(14) | BIT(10))
|
||||
|
||||
#define TSTCNTL_WRITE(bank, reg) (TSTCNTL_WR | ((bank) << TSTCNTL_BANK_SEL) \
|
||||
| ((reg) << TSTCNTL_WRITE_ADDR))
|
||||
#define TSTCNTL_READ(bank, reg) (TSTCNTL_RD | ((bank) << TSTCNTL_BANK_SEL) \
|
||||
| ((reg) << TSTCNTL_READ_ADDR))
|
||||
|
||||
#define TSTMODE_ENABLE 0x400
|
||||
#define TSTMODE_DISABLE 0x0
|
||||
#define CFGCNTL_WRITE(group, reg) (CFGCNTL_WR | ((group) << CFGCNTL_GROUP_SEL) \
|
||||
| ((reg) << CFGCNTL_WRITE_ADDR))
|
||||
#define CFGCNTL_READ(group, reg) (CFGCNTL_RD | ((group) << CFGCNTL_GROUP_SEL) \
|
||||
| ((reg) << CFGCNTL_READ_ADDR))
|
||||
|
||||
#define GAIN_PRE GENMASK(5, 2)
|
||||
#define WR_ADDR_A7CFG 0x18
|
||||
|
||||
enum {
|
||||
BANK_DSP0 = 0,
|
||||
BANK_WOL,
|
||||
BANK_BIST = 3,
|
||||
BANK_AFE,
|
||||
BANK_DSP1
|
||||
GROUP_CFG0 = 0,
|
||||
GROUP_WOL,
|
||||
GROUP_CFG0_READ,
|
||||
GROUP_BIST,
|
||||
GROUP_AFE,
|
||||
GROUP_CFG1
|
||||
};
|
||||
|
||||
struct rockchip_fephy_priv {
|
||||
struct phy_device *phydev;
|
||||
unsigned int clk_rate;
|
||||
int old_link;
|
||||
int wol_irq;
|
||||
int current_group;
|
||||
};
|
||||
|
||||
static int rockchip_fephy_init_tstmode(struct phy_device *phydev)
|
||||
static int rockchip_fephy_group_read(struct phy_device *phydev, u8 group, u32 reg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_DISABLE);
|
||||
ret = phy_write(phydev, SMI_ADDR_CFGCNTL, CFGCNTL_READ(group, reg));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_ENABLE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_DISABLE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_ENABLE);
|
||||
if (group)
|
||||
return phy_read(phydev, SMI_ADDR_TSTREAD1);
|
||||
else
|
||||
return (phy_read(phydev, SMI_ADDR_TSTREAD1) |
|
||||
(phy_read(phydev, SMI_ADDR_TSTREAD2) << 16));
|
||||
}
|
||||
|
||||
static int rockchip_fephy_close_tstmode(struct phy_device *phydev)
|
||||
{
|
||||
/* Back to basic register bank */
|
||||
return phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_DISABLE);
|
||||
}
|
||||
|
||||
static int rockchip_fephy_bank_write(struct phy_device *phydev, u8 bank,
|
||||
u32 reg, u16 val)
|
||||
static int rockchip_fephy_group_write(struct phy_device *phydev, u8 group,
|
||||
u32 reg, u16 val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@@ -96,11 +89,12 @@ static int rockchip_fephy_bank_write(struct phy_device *phydev, u8 bank,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return phy_write(phydev, SMI_ADDR_TSTCNTL, TSTCNTL_WRITE(bank, reg));
|
||||
return phy_write(phydev, SMI_ADDR_CFGCNTL, CFGCNTL_WRITE(group, reg));
|
||||
}
|
||||
|
||||
static int rockchip_fephy_config_init(struct phy_device *phydev)
|
||||
{
|
||||
struct rockchip_fephy_priv *priv = phydev->priv;
|
||||
int ret;
|
||||
|
||||
/* LED Control, default:0x7f */
|
||||
@@ -108,23 +102,32 @@ static int rockchip_fephy_config_init(struct phy_device *phydev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = rockchip_fephy_init_tstmode(phydev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* off-energy level0 threshold */
|
||||
ret = rockchip_fephy_bank_write(phydev, BANK_DSP0, 0xa, 0x6664);
|
||||
ret = rockchip_fephy_group_write(phydev, GROUP_CFG0, 0xa, 0x6664);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* 100M amplitude control */
|
||||
ret = rockchip_fephy_bank_write(phydev, BANK_DSP0, 0x18, 0xc);
|
||||
ret = rockchip_fephy_group_write(phydev, GROUP_CFG0, 0x18, 0xc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = rockchip_fephy_close_tstmode(phydev);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (priv->clk_rate == 24000000) {
|
||||
int sel;
|
||||
|
||||
/* pll cp cur sel */
|
||||
sel = rockchip_fephy_group_read(phydev, GROUP_AFE, 0x3);
|
||||
if (sel < 0)
|
||||
return sel;
|
||||
ret = rockchip_fephy_group_write(phydev, GROUP_AFE, 0x3, sel | 0x2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* pll lpf res sel */
|
||||
ret = rockchip_fephy_group_write(phydev, GROUP_CFG0, 0x1a, 0x6);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -134,27 +137,53 @@ static int rockchip_fephy_config_aneg(struct phy_device *phydev)
|
||||
return genphy_config_aneg(phydev);
|
||||
}
|
||||
|
||||
static void rockchip_feph_link_change_notify(struct phy_device *phydev)
|
||||
{
|
||||
struct rockchip_fephy_priv *priv = phydev->priv;
|
||||
int ret;
|
||||
|
||||
if (priv->old_link && !phydev->link) {
|
||||
priv->old_link = 0;
|
||||
ret = rockchip_fephy_group_write(phydev, GROUP_CFG0, 0xa, 0x6664);
|
||||
if (ret)
|
||||
return;
|
||||
} else if (!priv->old_link && phydev->link) {
|
||||
int gain;
|
||||
|
||||
priv->old_link = 1;
|
||||
/* read gain level */
|
||||
gain = rockchip_fephy_group_read(phydev, GROUP_CFG0, 0x0);
|
||||
if (gain < 0)
|
||||
return;
|
||||
if (!(gain & GAIN_PRE)) {
|
||||
ret = rockchip_fephy_group_write(phydev, GROUP_CFG0, 0xa, 0x6666);
|
||||
if (ret)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int rockchip_fephy_wol_enable(struct phy_device *phydev)
|
||||
{
|
||||
struct net_device *ndev = phydev->attached_dev;
|
||||
int ret;
|
||||
|
||||
ret = rockchip_fephy_bank_write(phydev, BANK_WOL, 0x0,
|
||||
((u16)ndev->dev_addr[4] << 8) + ndev->dev_addr[5]);
|
||||
ret = rockchip_fephy_group_write(phydev, GROUP_WOL, 0x0,
|
||||
((u16)ndev->dev_addr[4] << 8) + ndev->dev_addr[5]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = rockchip_fephy_bank_write(phydev, BANK_WOL, 0x1,
|
||||
((u16)ndev->dev_addr[2] << 8) + ndev->dev_addr[3]);
|
||||
ret = rockchip_fephy_group_write(phydev, GROUP_WOL, 0x1,
|
||||
((u16)ndev->dev_addr[2] << 8) + ndev->dev_addr[3]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = rockchip_fephy_bank_write(phydev, BANK_WOL, 0x2,
|
||||
((u16)ndev->dev_addr[0] << 8) + ndev->dev_addr[1]);
|
||||
ret = rockchip_fephy_group_write(phydev, GROUP_WOL, 0x2,
|
||||
((u16)ndev->dev_addr[0] << 8) + ndev->dev_addr[1]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = rockchip_fephy_bank_write(phydev, BANK_WOL, 0x3, 0xf);
|
||||
ret = rockchip_fephy_group_write(phydev, GROUP_WOL, 0x3, 0xf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -170,7 +199,7 @@ static int rockchip_fephy_wol_disable(struct phy_device *phydev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = rockchip_fephy_bank_write(phydev, BANK_WOL, 0x3, 0x0);
|
||||
ret = rockchip_fephy_group_write(phydev, GROUP_WOL, 0x3, 0x0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -192,6 +221,355 @@ static irqreturn_t rockchip_fephy_wol_irq_thread(int irq, void *dev_id)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void rockchip_fephy_dump_cfg1_group_regs(struct phy_device *phydev, int group, char *buf)
|
||||
{
|
||||
int reg = 0, val = 0;
|
||||
|
||||
for (reg = 0; reg < 18; reg++) {
|
||||
val = rockchip_fephy_group_read(phydev, GROUP_CFG1, reg);
|
||||
if (val < 0) {
|
||||
pr_err("group%d %2d read error: %d\n", group, reg, val);
|
||||
return;
|
||||
}
|
||||
if (buf)
|
||||
sprintf(buf, "%sgroup%d %2d: 0x%x\n", buf, group, reg, val);
|
||||
else
|
||||
pr_info("group%d reg_%02d: 0x%x\n", group, reg, val);
|
||||
}
|
||||
}
|
||||
|
||||
static void rockchip_fephy_dump_afe_group_regs(struct phy_device *phydev, int group, char *buf)
|
||||
{
|
||||
int reg = 0, val = 0;
|
||||
|
||||
for (reg = 0; reg < 32; reg++) {
|
||||
val = rockchip_fephy_group_read(phydev, GROUP_AFE, reg);
|
||||
if (val < 0) {
|
||||
pr_err("group%d %2d read error: %d\n", group, reg, val);
|
||||
return;
|
||||
}
|
||||
if (buf)
|
||||
sprintf(buf, "%sgroup%d %2d: 0x%x\n", buf, group, reg, val);
|
||||
else
|
||||
pr_info("group%d reg_%02d: 0x%x\n", group, reg, val);
|
||||
}
|
||||
}
|
||||
|
||||
static void rockchip_fephy_dump_bist_group_regs(struct phy_device *phydev, int group, char *buf)
|
||||
{
|
||||
int reg = 0, val = 0;
|
||||
|
||||
for (reg = 0; reg < 32; reg++) {
|
||||
val = rockchip_fephy_group_read(phydev, GROUP_BIST, reg);
|
||||
if (val < 0) {
|
||||
pr_err("group%d %2d read error: %d\n", group, reg, val);
|
||||
return;
|
||||
}
|
||||
if (buf)
|
||||
sprintf(buf, "%sgroup%d %2d: 0x%x\n", buf, group, reg, val);
|
||||
else
|
||||
pr_info("group%d reg_%02d: 0x%x\n", group, reg, val);
|
||||
}
|
||||
}
|
||||
|
||||
static void rockchip_fephy_dump_cfg_read_group_regs(struct phy_device *phydev, int group, char *buf)
|
||||
{
|
||||
int reg = 0, val = 0;
|
||||
|
||||
for (reg = 0; reg < 32; reg++) {
|
||||
val = rockchip_fephy_group_read(phydev, GROUP_CFG0_READ, reg);
|
||||
if (val < 0) {
|
||||
pr_err("group%d %2d read error: %d\n", group, reg, val);
|
||||
return;
|
||||
}
|
||||
if (buf)
|
||||
sprintf(buf, "%sgroup%d %2d: 0x%x\n", buf, group, reg, val);
|
||||
else
|
||||
pr_info("group%d reg_%02d: 0x%x\n", group, reg, val);
|
||||
}
|
||||
}
|
||||
|
||||
static void rockchip_fephy_dump_wol_group_regs(struct phy_device *phydev, int group, char *buf)
|
||||
{
|
||||
int reg = 0, val = 0;
|
||||
|
||||
for (reg = 0; reg < 13; reg++) {
|
||||
val = rockchip_fephy_group_read(phydev, GROUP_WOL, reg);
|
||||
if (val < 0) {
|
||||
pr_err("group%d %2d read error: %d\n", group, reg, val);
|
||||
return;
|
||||
}
|
||||
if (buf)
|
||||
sprintf(buf, "%sgroup%d %2d: 0x%x\n", buf, group, reg, val);
|
||||
else
|
||||
pr_info("group%d reg_%02d: 0x%x\n", group, reg, val);
|
||||
}
|
||||
}
|
||||
|
||||
static void rockchip_fephy_dump_cfg_group_regs(struct phy_device *phydev, int group, char *buf)
|
||||
{
|
||||
int reg = 0, val = 0;
|
||||
|
||||
for (reg = 0; reg < 32; reg++) {
|
||||
val = rockchip_fephy_group_read(phydev, GROUP_CFG0, reg);
|
||||
if (val < 0) {
|
||||
pr_err("group%d %2d read error: %d\n", group, reg, val);
|
||||
return;
|
||||
}
|
||||
if (buf)
|
||||
sprintf(buf, "%sgroup%d %2d: 0x%x\n", buf, group, reg, val);
|
||||
else
|
||||
pr_info("group%d reg_%02d: 0x%x\n", group, reg, val);
|
||||
}
|
||||
}
|
||||
|
||||
static void rockchip_fephy_phy_read_priv_reg(struct phy_device *phydev, int group, int reg)
|
||||
{
|
||||
int val = 0;
|
||||
|
||||
switch (group) {
|
||||
case GROUP_CFG0: /* CFG0 register group */
|
||||
val = rockchip_fephy_group_read(phydev, GROUP_CFG0, reg);
|
||||
if (val < 0) {
|
||||
pr_err("group%d %2d read error: %d\n", group, reg, val);
|
||||
return;
|
||||
}
|
||||
pr_info("read group%d reg_%02d: 0x%x\n", group, reg, val);
|
||||
break;
|
||||
case GROUP_WOL: /* WOL register group */
|
||||
val = rockchip_fephy_group_read(phydev, GROUP_WOL, reg);
|
||||
if (val < 0) {
|
||||
pr_err("group%d %2d read error: %d\n", group, reg, val);
|
||||
return;
|
||||
}
|
||||
pr_info("read group%d reg_%02d: 0x%x\n", group, reg, val);
|
||||
break;
|
||||
case GROUP_CFG0_READ: /* CFG0_read register group */
|
||||
val = rockchip_fephy_group_read(phydev, GROUP_CFG0_READ, reg);
|
||||
if (val < 0) {
|
||||
pr_err("group%d %2d read error: %d\n", group, reg, val);
|
||||
return;
|
||||
}
|
||||
pr_info("read group%d reg_%02d: 0x%x\n", group, reg, val);
|
||||
break;
|
||||
case GROUP_BIST: /* BIST register group */
|
||||
val = rockchip_fephy_group_read(phydev, GROUP_BIST, reg);
|
||||
if (val < 0) {
|
||||
pr_err("group%d %2d read error: %d\n", group, reg, val);
|
||||
return;
|
||||
}
|
||||
pr_info("read group%d reg_%02d: 0x%x\n", group, reg, val);
|
||||
break;
|
||||
case GROUP_AFE: /* AFE register group */
|
||||
val = rockchip_fephy_group_read(phydev, GROUP_AFE, reg);
|
||||
if (val < 0) {
|
||||
pr_err("group%d %2d read error: %d\n", group, reg, val);
|
||||
return;
|
||||
}
|
||||
pr_info("read group%d reg_%02d: 0x%x\n", group, reg, val);
|
||||
break;
|
||||
case GROUP_CFG1: /* CFG1 register group */
|
||||
val = rockchip_fephy_group_read(phydev, GROUP_CFG1, reg);
|
||||
if (val < 0) {
|
||||
pr_err("group%d %2d read error: %d\n", group, reg, val);
|
||||
return;
|
||||
}
|
||||
pr_info("read group%d reg_%02d: 0x%x\n", group, reg, val);
|
||||
break;
|
||||
default:
|
||||
pr_err("error group num: %d\n", group);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
rockchip_fephy_phy_write_priv_reg(struct phy_device *phydev, int group, int reg, int rval)
|
||||
{
|
||||
int val = 0;
|
||||
|
||||
switch (group) {
|
||||
case GROUP_CFG0: /* CFG0 register group */
|
||||
val = rockchip_fephy_group_write(phydev, GROUP_CFG0, reg, rval);
|
||||
if (val) {
|
||||
pr_err("group%d %2d write error: %d\n", group, reg, val);
|
||||
return;
|
||||
}
|
||||
pr_info("write group%d reg_%02d: 0x%x\n", group, reg, val);
|
||||
break;
|
||||
case GROUP_WOL: /* WOL register group */
|
||||
val = rockchip_fephy_group_write(phydev, GROUP_WOL, reg, rval);
|
||||
if (val) {
|
||||
pr_err("group%d %2d write error: %d\n", group, reg, val);
|
||||
return;
|
||||
}
|
||||
pr_info("write group%d reg_%02d: 0x%x\n", group, reg, val);
|
||||
break;
|
||||
case GROUP_CFG0_READ: /* CFG0_read register group */
|
||||
val = rockchip_fephy_group_write(phydev, GROUP_CFG0_READ, reg, rval);
|
||||
if (val) {
|
||||
pr_err("group%d %2d write error: %d\n", group, reg, val);
|
||||
return;
|
||||
}
|
||||
pr_info("write group%d reg_%02d: 0x%x\n", group, reg, val);
|
||||
break;
|
||||
case GROUP_BIST: /* BIST register group */
|
||||
val = rockchip_fephy_group_write(phydev, GROUP_BIST, reg, rval);
|
||||
if (val) {
|
||||
pr_err("group%d %2d write error: %d\n", group, reg, val);
|
||||
return;
|
||||
}
|
||||
pr_info("write group%d reg_%02d: 0x%x\n", group, reg, val);
|
||||
break;
|
||||
case GROUP_AFE: /* AFE register group */
|
||||
val = rockchip_fephy_group_write(phydev, GROUP_AFE, reg, rval);
|
||||
if (val) {
|
||||
pr_err("group%d %2d write error: %d\n", group, reg, val);
|
||||
return;
|
||||
}
|
||||
pr_info("write group%d reg_%02d: 0x%x\n", group, reg, val);
|
||||
break;
|
||||
case GROUP_CFG1: /* CFG1 register group */
|
||||
val = rockchip_fephy_group_write(phydev, GROUP_CFG1, reg, rval);
|
||||
if (val) {
|
||||
pr_err("group%d %2d write error: %d\n", group, reg, val);
|
||||
return;
|
||||
}
|
||||
pr_info("write group%d reg_%02d: 0x%x\n", group, reg, val);
|
||||
break;
|
||||
default:
|
||||
pr_err("error group num: %d\n", group);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
phy_param_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct phy_device *phydev = to_phy_device(dev);
|
||||
struct rockchip_fephy_priv *priv = phydev->priv;
|
||||
|
||||
switch (priv->current_group) {
|
||||
case GROUP_CFG0:
|
||||
rockchip_fephy_dump_cfg_group_regs(phydev, GROUP_CFG0, buf);
|
||||
break;
|
||||
case GROUP_WOL:
|
||||
rockchip_fephy_dump_wol_group_regs(phydev, GROUP_WOL, buf);
|
||||
break;
|
||||
case GROUP_CFG0_READ:
|
||||
rockchip_fephy_dump_cfg_read_group_regs(phydev, GROUP_CFG0_READ, buf);
|
||||
break;
|
||||
case GROUP_BIST:
|
||||
rockchip_fephy_dump_bist_group_regs(phydev, GROUP_BIST, buf);
|
||||
break;
|
||||
case GROUP_AFE:
|
||||
rockchip_fephy_dump_afe_group_regs(phydev, GROUP_AFE, buf);
|
||||
break;
|
||||
case GROUP_CFG1:
|
||||
rockchip_fephy_dump_cfg1_group_regs(phydev, GROUP_CFG1, buf);
|
||||
break;
|
||||
default:
|
||||
pr_err("error group num: %d\n", priv->current_group);
|
||||
break;
|
||||
}
|
||||
|
||||
return strlen(buf);
|
||||
}
|
||||
|
||||
static ssize_t phy_param_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct phy_device *phydev = to_phy_device(dev);
|
||||
struct rockchip_fephy_priv *priv = phydev->priv;
|
||||
int arg1 = 0, arg2 = 0, arg3 = 0, ret;
|
||||
char *buff, *p, *para;
|
||||
char *argv[4];
|
||||
int argc;
|
||||
char cmd;
|
||||
|
||||
buff = kstrdup(buf, GFP_KERNEL);
|
||||
if (!buff)
|
||||
return -EINVAL;
|
||||
|
||||
p = buff;
|
||||
for (argc = 0; argc < 4; argc++) {
|
||||
para = strsep(&p, " ");
|
||||
if (!para) {
|
||||
argv[argc] = NULL;
|
||||
continue;
|
||||
}
|
||||
argv[argc] = para;
|
||||
}
|
||||
if (argc < 1 || argc > 4)
|
||||
goto end;
|
||||
|
||||
if (argv[1]) {
|
||||
ret = kstrtoint(argv[1], 0, &arg1);
|
||||
if (ret)
|
||||
pr_err("kstrtoint failed\n");
|
||||
}
|
||||
if (argv[2]) {
|
||||
ret = kstrtoint(argv[2], 0, &arg2);
|
||||
if (ret)
|
||||
pr_err("kstrtoint failed\n");
|
||||
}
|
||||
if (argv[3]) {
|
||||
ret = kstrtoint(argv[3], 0, &arg3);
|
||||
if (ret)
|
||||
pr_err("kstrtoint failed\n");
|
||||
}
|
||||
|
||||
cmd = argv[0][0];
|
||||
switch (cmd) {
|
||||
case 'R':
|
||||
rockchip_fephy_phy_read_priv_reg(phydev, arg1, arg2);
|
||||
priv->current_group = arg1;
|
||||
break;
|
||||
case 'W':
|
||||
rockchip_fephy_phy_write_priv_reg(phydev, arg1, arg2, arg3);
|
||||
priv->current_group = arg1;
|
||||
break;
|
||||
case 'd':
|
||||
priv->current_group = GROUP_CFG0;
|
||||
rockchip_fephy_dump_cfg_group_regs(phydev, GROUP_CFG0, NULL);
|
||||
break;
|
||||
case 'w':
|
||||
priv->current_group = GROUP_WOL;
|
||||
rockchip_fephy_dump_wol_group_regs(phydev, GROUP_WOL, NULL);
|
||||
break;
|
||||
case 'p':
|
||||
priv->current_group = GROUP_CFG0_READ;
|
||||
rockchip_fephy_dump_cfg_read_group_regs(phydev, GROUP_CFG0_READ, NULL);
|
||||
break;
|
||||
case 'b':
|
||||
priv->current_group = GROUP_BIST;
|
||||
rockchip_fephy_dump_bist_group_regs(phydev, GROUP_BIST, NULL);
|
||||
break;
|
||||
case 'a':
|
||||
priv->current_group = GROUP_AFE;
|
||||
rockchip_fephy_dump_afe_group_regs(phydev, GROUP_AFE, NULL);
|
||||
break;
|
||||
case 's':
|
||||
priv->current_group = GROUP_CFG1;
|
||||
rockchip_fephy_dump_cfg1_group_regs(phydev, GROUP_CFG1, NULL);
|
||||
break;
|
||||
case 'r':
|
||||
priv->current_group = GROUP_CFG0;
|
||||
if (phydev && phydev->drv->soft_reset)
|
||||
phydev->drv->soft_reset(phydev);
|
||||
break;
|
||||
default:
|
||||
goto end;
|
||||
}
|
||||
|
||||
return count;
|
||||
|
||||
end:
|
||||
kfree(buff);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(phy_param);
|
||||
|
||||
static int rockchip_fephy_probe(struct phy_device *phydev)
|
||||
{
|
||||
struct rockchip_fephy_priv *priv;
|
||||
@@ -202,6 +580,9 @@ static int rockchip_fephy_probe(struct phy_device *phydev)
|
||||
return -ENOMEM;
|
||||
|
||||
phydev->priv = priv;
|
||||
if (device_property_read_u32(&phydev->mdio.dev, "clock-frequency", &priv->clk_rate))
|
||||
priv->clk_rate = 24000000;
|
||||
|
||||
priv->wol_irq = platform_get_irq_byname_optional(to_platform_device(&phydev->mdio.dev),
|
||||
"wol_irq");
|
||||
if (priv->wol_irq == -EPROBE_DEFER)
|
||||
@@ -220,6 +601,11 @@ static int rockchip_fephy_probe(struct phy_device *phydev)
|
||||
}
|
||||
|
||||
priv->phydev = phydev;
|
||||
|
||||
ret = device_create_file(&phydev->mdio.dev, &dev_attr_phy_param);
|
||||
if (ret)
|
||||
goto irq_err;
|
||||
|
||||
return 0;
|
||||
|
||||
irq_err:
|
||||
@@ -229,6 +615,7 @@ irq_err:
|
||||
|
||||
static void rockchip_fephy_remove(struct phy_device *phydev)
|
||||
{
|
||||
device_remove_file(&phydev->mdio.dev, &dev_attr_phy_param);
|
||||
}
|
||||
|
||||
static int rockchip_fephy_suspend(struct phy_device *phydev)
|
||||
@@ -263,6 +650,7 @@ static struct phy_driver rockchip_fephy_driver[] = {
|
||||
/* PHY_BASIC_FEATURES */
|
||||
.features = PHY_BASIC_FEATURES,
|
||||
.flags = 0,
|
||||
.link_change_notify = rockchip_feph_link_change_notify,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
.config_init = rockchip_fephy_config_init,
|
||||
.config_aneg = rockchip_fephy_config_aneg,
|
||||
|
||||
Reference in New Issue
Block a user