diff --git a/Documentation/devicetree/bindings/i2c/i2c-rk3x.yaml b/Documentation/devicetree/bindings/i2c/i2c-rk3x.yaml index 82b9d6682297..90b90a24e0df 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-rk3x.yaml +++ b/Documentation/devicetree/bindings/i2c/i2c-rk3x.yaml @@ -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 diff --git a/arch/arm64/boot/dts/rockchip/rv1126b-evb1-v10.dtsi b/arch/arm64/boot/dts/rockchip/rv1126b-evb1-v10.dtsi index 5c3fed632304..262b8c2674a2 100644 --- a/arch/arm64/boot/dts/rockchip/rv1126b-evb1-v10.dtsi +++ b/arch/arm64/boot/dts/rockchip/rv1126b-evb1-v10.dtsi @@ -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>; diff --git a/arch/arm64/boot/dts/rockchip/rv1126b-evb2-v10.dts b/arch/arm64/boot/dts/rockchip/rv1126b-evb2-v10.dts index f3a7677ba283..24d56147893f 100644 --- a/arch/arm64/boot/dts/rockchip/rv1126b-evb2-v10.dts +++ b/arch/arm64/boot/dts/rockchip/rv1126b-evb2-v10.dts @@ -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>; diff --git a/arch/arm64/boot/dts/rockchip/rv1126b-iotest-v10.dts b/arch/arm64/boot/dts/rockchip/rv1126b-iotest-v10.dts index 7655959ea5ef..4f3ea4610f34 100644 --- a/arch/arm64/boot/dts/rockchip/rv1126b-iotest-v10.dts +++ b/arch/arm64/boot/dts/rockchip/rv1126b-iotest-v10.dts @@ -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; }; diff --git a/arch/arm64/boot/dts/rockchip/rv1126b.dtsi b/arch/arm64/boot/dts/rockchip/rv1126b.dtsi index 43d450d2f2ca..d07837421cda 100644 --- a/arch/arm64/boot/dts/rockchip/rv1126b.dtsi +++ b/arch/arm64/boot/dts/rockchip/rv1126b.dtsi @@ -1576,7 +1576,7 @@ }; i2c2: i2c@20800000 { - compatible = "rockchip,rv1126b-i2c", "rockchip,rk3399-i2c"; + compatible = "rockchip,rv1126b-i2c"; reg = <0x20800000 0x1000>; interrupts = ; #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 = ; #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 = ; #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 = ; #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 = ; #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 = ; #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"; diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c index 927d9ea6c597..37837e58193c 100644 --- a/drivers/i2c/busses/i2c-rk3x.c +++ b/drivers/i2c/busses/i2c-rk3x.c @@ -7,6 +7,8 @@ */ #include +#include +#include #include #include #include @@ -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); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c index 9c2d790d3a90..729740e02e2d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c @@ -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); diff --git a/drivers/net/phy/rockchip-fephy.c b/drivers/net/phy/rockchip-fephy.c index 6c5ac91a0c77..1b7fdd4685a4 100644 --- a/drivers/net/phy/rockchip-fephy.c +++ b/drivers/net/phy/rockchip-fephy.c @@ -13,12 +13,13 @@ #include #include #include +#include #include #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,