diff options
author | Laxman Dewangan <ldewangan@nvidia.com> | 2013-01-05 07:04:46 -0500 |
---|---|---|
committer | Wolfram Sang <w.sang@pengutronix.de> | 2013-01-27 23:26:43 -0500 |
commit | 2a2897bab2d3d50ab466cf908f03b62801f1ff56 (patch) | |
tree | acd8aa07d9a33368e5bd3223d8d9207b5447f9ae /drivers/i2c | |
parent | b61b14154b19e1ef1da9c1e283f0cf93470e0c70 (diff) |
i2c: tegra: add support for Tegra114 SoC
NVIDIA's Tegra114 has following enhanced feature in i2c controller:
- Enable/disable control for per packet transfer complete interrupt.
Earlier SoCs could not disable this.
- Single clock source for standard/fast and HS mode clock speed.
The clock divisor for fast/standard mode is added into the i2c
controller to meet the HS and standard/fast mode of clock speed
from single source.
Add support for the above feature to make it functional on T114 SOCs.
Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
Reviewed-by: Stephen Warren <swarren@nvidia.com>
Signed-off-by: Wolfram Sang <w.sang@pengutronix.de>
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/busses/i2c-tegra.c | 77 |
1 files changed, 63 insertions, 14 deletions
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 7b38877ffec1..2dadb964c464 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c | |||
@@ -71,6 +71,8 @@ | |||
71 | #define I2C_INT_TX_FIFO_DATA_REQ (1<<1) | 71 | #define I2C_INT_TX_FIFO_DATA_REQ (1<<1) |
72 | #define I2C_INT_RX_FIFO_DATA_REQ (1<<0) | 72 | #define I2C_INT_RX_FIFO_DATA_REQ (1<<0) |
73 | #define I2C_CLK_DIVISOR 0x06c | 73 | #define I2C_CLK_DIVISOR 0x06c |
74 | #define I2C_CLK_DIVISOR_STD_FAST_MODE_SHIFT 16 | ||
75 | #define I2C_CLK_MULTIPLIER_STD_FAST_MODE 8 | ||
74 | 76 | ||
75 | #define DVC_CTRL_REG1 0x000 | 77 | #define DVC_CTRL_REG1 0x000 |
76 | #define DVC_CTRL_REG1_INTR_EN (1<<10) | 78 | #define DVC_CTRL_REG1_INTR_EN (1<<10) |
@@ -117,10 +119,23 @@ enum msg_end_type { | |||
117 | /** | 119 | /** |
118 | * struct tegra_i2c_hw_feature : Different HW support on Tegra | 120 | * struct tegra_i2c_hw_feature : Different HW support on Tegra |
119 | * @has_continue_xfer_support: Continue transfer supports. | 121 | * @has_continue_xfer_support: Continue transfer supports. |
122 | * @has_per_pkt_xfer_complete_irq: Has enable/disable capability for transfer | ||
123 | * complete interrupt per packet basis. | ||
124 | * @has_single_clk_source: The i2c controller has single clock source. Tegra30 | ||
125 | * and earlier Socs has two clock sources i.e. div-clk and | ||
126 | * fast-clk. | ||
127 | * @clk_divisor_hs_mode: Clock divisor in HS mode. | ||
128 | * @clk_divisor_std_fast_mode: Clock divisor in standard/fast mode. It is | ||
129 | * applicable if there is no fast clock source i.e. single clock | ||
130 | * source. | ||
120 | */ | 131 | */ |
121 | 132 | ||
122 | struct tegra_i2c_hw_feature { | 133 | struct tegra_i2c_hw_feature { |
123 | bool has_continue_xfer_support; | 134 | bool has_continue_xfer_support; |
135 | bool has_per_pkt_xfer_complete_irq; | ||
136 | bool has_single_clk_source; | ||
137 | int clk_divisor_hs_mode; | ||
138 | int clk_divisor_std_fast_mode; | ||
124 | }; | 139 | }; |
125 | 140 | ||
126 | /** | 141 | /** |
@@ -366,11 +381,13 @@ static void tegra_dvc_init(struct tegra_i2c_dev *i2c_dev) | |||
366 | static inline int tegra_i2c_clock_enable(struct tegra_i2c_dev *i2c_dev) | 381 | static inline int tegra_i2c_clock_enable(struct tegra_i2c_dev *i2c_dev) |
367 | { | 382 | { |
368 | int ret; | 383 | int ret; |
369 | ret = clk_prepare_enable(i2c_dev->fast_clk); | 384 | if (!i2c_dev->hw->has_single_clk_source) { |
370 | if (ret < 0) { | 385 | ret = clk_prepare_enable(i2c_dev->fast_clk); |
371 | dev_err(i2c_dev->dev, | 386 | if (ret < 0) { |
372 | "Enabling fast clk failed, err %d\n", ret); | 387 | dev_err(i2c_dev->dev, |
373 | return ret; | 388 | "Enabling fast clk failed, err %d\n", ret); |
389 | return ret; | ||
390 | } | ||
374 | } | 391 | } |
375 | ret = clk_prepare_enable(i2c_dev->div_clk); | 392 | ret = clk_prepare_enable(i2c_dev->div_clk); |
376 | if (ret < 0) { | 393 | if (ret < 0) { |
@@ -384,13 +401,16 @@ static inline int tegra_i2c_clock_enable(struct tegra_i2c_dev *i2c_dev) | |||
384 | static inline void tegra_i2c_clock_disable(struct tegra_i2c_dev *i2c_dev) | 401 | static inline void tegra_i2c_clock_disable(struct tegra_i2c_dev *i2c_dev) |
385 | { | 402 | { |
386 | clk_disable_unprepare(i2c_dev->div_clk); | 403 | clk_disable_unprepare(i2c_dev->div_clk); |
387 | clk_disable_unprepare(i2c_dev->fast_clk); | 404 | if (!i2c_dev->hw->has_single_clk_source) |
405 | clk_disable_unprepare(i2c_dev->fast_clk); | ||
388 | } | 406 | } |
389 | 407 | ||
390 | static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) | 408 | static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) |
391 | { | 409 | { |
392 | u32 val; | 410 | u32 val; |
393 | int err = 0; | 411 | int err = 0; |
412 | int clk_multiplier = I2C_CLK_MULTIPLIER_STD_FAST_MODE; | ||
413 | u32 clk_divisor; | ||
394 | 414 | ||
395 | tegra_i2c_clock_enable(i2c_dev); | 415 | tegra_i2c_clock_enable(i2c_dev); |
396 | 416 | ||
@@ -405,7 +425,15 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev) | |||
405 | (0x2 << I2C_CNFG_DEBOUNCE_CNT_SHIFT); | 425 | (0x2 << I2C_CNFG_DEBOUNCE_CNT_SHIFT); |
406 | i2c_writel(i2c_dev, val, I2C_CNFG); | 426 | i2c_writel(i2c_dev, val, I2C_CNFG); |
407 | i2c_writel(i2c_dev, 0, I2C_INT_MASK); | 427 | i2c_writel(i2c_dev, 0, I2C_INT_MASK); |
408 | clk_set_rate(i2c_dev->div_clk, i2c_dev->bus_clk_rate * 8); | 428 | |
429 | clk_multiplier *= (i2c_dev->hw->clk_divisor_std_fast_mode + 1); | ||
430 | clk_set_rate(i2c_dev->div_clk, i2c_dev->bus_clk_rate * clk_multiplier); | ||
431 | |||
432 | /* Make sure clock divisor programmed correctly */ | ||
433 | clk_divisor = i2c_dev->hw->clk_divisor_hs_mode; | ||
434 | clk_divisor |= i2c_dev->hw->clk_divisor_std_fast_mode << | ||
435 | I2C_CLK_DIVISOR_STD_FAST_MODE_SHIFT; | ||
436 | i2c_writel(i2c_dev, clk_divisor, I2C_CLK_DIVISOR); | ||
409 | 437 | ||
410 | if (!i2c_dev->is_dvc) { | 438 | if (!i2c_dev->is_dvc) { |
411 | u32 sl_cfg = i2c_readl(i2c_dev, I2C_SL_CNFG); | 439 | u32 sl_cfg = i2c_readl(i2c_dev, I2C_SL_CNFG); |
@@ -547,6 +575,8 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, | |||
547 | tegra_i2c_fill_tx_fifo(i2c_dev); | 575 | tegra_i2c_fill_tx_fifo(i2c_dev); |
548 | 576 | ||
549 | int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST; | 577 | int_mask = I2C_INT_NO_ACK | I2C_INT_ARBITRATION_LOST; |
578 | if (i2c_dev->hw->has_per_pkt_xfer_complete_irq) | ||
579 | int_mask |= I2C_INT_PACKET_XFER_COMPLETE; | ||
550 | if (msg->flags & I2C_M_RD) | 580 | if (msg->flags & I2C_M_RD) |
551 | int_mask |= I2C_INT_RX_FIFO_DATA_REQ; | 581 | int_mask |= I2C_INT_RX_FIFO_DATA_REQ; |
552 | else if (i2c_dev->msg_buf_remaining) | 582 | else if (i2c_dev->msg_buf_remaining) |
@@ -634,15 +664,32 @@ static const struct i2c_algorithm tegra_i2c_algo = { | |||
634 | 664 | ||
635 | static const struct tegra_i2c_hw_feature tegra20_i2c_hw = { | 665 | static const struct tegra_i2c_hw_feature tegra20_i2c_hw = { |
636 | .has_continue_xfer_support = false, | 666 | .has_continue_xfer_support = false, |
667 | .has_per_pkt_xfer_complete_irq = false, | ||
668 | .has_single_clk_source = false, | ||
669 | .clk_divisor_hs_mode = 3, | ||
670 | .clk_divisor_std_fast_mode = 0, | ||
637 | }; | 671 | }; |
638 | 672 | ||
639 | static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { | 673 | static const struct tegra_i2c_hw_feature tegra30_i2c_hw = { |
640 | .has_continue_xfer_support = true, | 674 | .has_continue_xfer_support = true, |
675 | .has_per_pkt_xfer_complete_irq = false, | ||
676 | .has_single_clk_source = false, | ||
677 | .clk_divisor_hs_mode = 3, | ||
678 | .clk_divisor_std_fast_mode = 0, | ||
679 | }; | ||
680 | |||
681 | static const struct tegra_i2c_hw_feature tegra114_i2c_hw = { | ||
682 | .has_continue_xfer_support = true, | ||
683 | .has_per_pkt_xfer_complete_irq = true, | ||
684 | .has_single_clk_source = true, | ||
685 | .clk_divisor_hs_mode = 1, | ||
686 | .clk_divisor_std_fast_mode = 0x19, | ||
641 | }; | 687 | }; |
642 | 688 | ||
643 | #if defined(CONFIG_OF) | 689 | #if defined(CONFIG_OF) |
644 | /* Match table for of_platform binding */ | 690 | /* Match table for of_platform binding */ |
645 | static const struct of_device_id tegra_i2c_of_match[] = { | 691 | static const struct of_device_id tegra_i2c_of_match[] = { |
692 | { .compatible = "nvidia,tegra114-i2c", .data = &tegra114_i2c_hw, }, | ||
646 | { .compatible = "nvidia,tegra30-i2c", .data = &tegra30_i2c_hw, }, | 693 | { .compatible = "nvidia,tegra30-i2c", .data = &tegra30_i2c_hw, }, |
647 | { .compatible = "nvidia,tegra20-i2c", .data = &tegra20_i2c_hw, }, | 694 | { .compatible = "nvidia,tegra20-i2c", .data = &tegra20_i2c_hw, }, |
648 | { .compatible = "nvidia,tegra20-i2c-dvc", .data = &tegra20_i2c_hw, }, | 695 | { .compatible = "nvidia,tegra20-i2c-dvc", .data = &tegra20_i2c_hw, }, |
@@ -688,12 +735,6 @@ static int tegra_i2c_probe(struct platform_device *pdev) | |||
688 | return PTR_ERR(div_clk); | 735 | return PTR_ERR(div_clk); |
689 | } | 736 | } |
690 | 737 | ||
691 | fast_clk = devm_clk_get(&pdev->dev, "fast-clk"); | ||
692 | if (IS_ERR(fast_clk)) { | ||
693 | dev_err(&pdev->dev, "missing bus clock"); | ||
694 | return PTR_ERR(fast_clk); | ||
695 | } | ||
696 | |||
697 | i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL); | 738 | i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL); |
698 | if (!i2c_dev) { | 739 | if (!i2c_dev) { |
699 | dev_err(&pdev->dev, "Could not allocate struct tegra_i2c_dev"); | 740 | dev_err(&pdev->dev, "Could not allocate struct tegra_i2c_dev"); |
@@ -702,7 +743,6 @@ static int tegra_i2c_probe(struct platform_device *pdev) | |||
702 | 743 | ||
703 | i2c_dev->base = base; | 744 | i2c_dev->base = base; |
704 | i2c_dev->div_clk = div_clk; | 745 | i2c_dev->div_clk = div_clk; |
705 | i2c_dev->fast_clk = fast_clk; | ||
706 | i2c_dev->adapter.algo = &tegra_i2c_algo; | 746 | i2c_dev->adapter.algo = &tegra_i2c_algo; |
707 | i2c_dev->irq = irq; | 747 | i2c_dev->irq = irq; |
708 | i2c_dev->cont_id = pdev->id; | 748 | i2c_dev->cont_id = pdev->id; |
@@ -733,6 +773,15 @@ static int tegra_i2c_probe(struct platform_device *pdev) | |||
733 | } | 773 | } |
734 | init_completion(&i2c_dev->msg_complete); | 774 | init_completion(&i2c_dev->msg_complete); |
735 | 775 | ||
776 | if (!i2c_dev->hw->has_single_clk_source) { | ||
777 | fast_clk = devm_clk_get(&pdev->dev, "fast-clk"); | ||
778 | if (IS_ERR(fast_clk)) { | ||
779 | dev_err(&pdev->dev, "missing fast clock"); | ||
780 | return PTR_ERR(fast_clk); | ||
781 | } | ||
782 | i2c_dev->fast_clk = fast_clk; | ||
783 | } | ||
784 | |||
736 | platform_set_drvdata(pdev, i2c_dev); | 785 | platform_set_drvdata(pdev, i2c_dev); |
737 | 786 | ||
738 | ret = tegra_i2c_init(i2c_dev); | 787 | ret = tegra_i2c_init(i2c_dev); |