.. | .. |
---|
18 | 18 | */ |
---|
19 | 19 | |
---|
20 | 20 | #include <linux/clk-provider.h> |
---|
| 21 | +#include <linux/io.h> |
---|
21 | 22 | #include <linux/mfd/syscon.h> |
---|
22 | 23 | #include <linux/of.h> |
---|
23 | 24 | #include <linux/of_device.h> |
---|
.. | .. |
---|
56 | 57 | struct clk_periph_driver_data { |
---|
57 | 58 | struct clk_hw_onecell_data *hw_data; |
---|
58 | 59 | spinlock_t lock; |
---|
| 60 | + void __iomem *reg; |
---|
| 61 | + |
---|
| 62 | + /* Storage registers for suspend/resume operations */ |
---|
| 63 | + u32 tbg_sel; |
---|
| 64 | + u32 div_sel0; |
---|
| 65 | + u32 div_sel1; |
---|
| 66 | + u32 div_sel2; |
---|
| 67 | + u32 clk_sel; |
---|
| 68 | + u32 clk_dis; |
---|
59 | 69 | }; |
---|
60 | 70 | |
---|
61 | 71 | struct clk_double_div { |
---|
.. | .. |
---|
294 | 304 | PERIPH_CLK_FULL_DD(sdio, 11, 14, DIV_SEL0, DIV_SEL0, 3, 6); |
---|
295 | 305 | PERIPH_CLK_FULL_DD(usb32_usb2_sys, 16, 16, DIV_SEL0, DIV_SEL0, 9, 12); |
---|
296 | 306 | PERIPH_CLK_FULL_DD(usb32_ss_sys, 17, 18, DIV_SEL0, DIV_SEL0, 15, 18); |
---|
| 307 | +static PERIPH_GATE(pcie, 14); |
---|
297 | 308 | |
---|
298 | 309 | static struct clk_periph_data data_sb[] = { |
---|
299 | 310 | REF_CLK_MUX_DD(gbe_50), |
---|
.. | .. |
---|
309 | 320 | REF_CLK_FULL_DD(sdio), |
---|
310 | 321 | REF_CLK_FULL_DD(usb32_usb2_sys), |
---|
311 | 322 | REF_CLK_FULL_DD(usb32_ss_sys), |
---|
| 323 | + REF_CLK_GATE(pcie, "gbe_core"), |
---|
312 | 324 | { }, |
---|
313 | 325 | }; |
---|
314 | 326 | |
---|
.. | .. |
---|
679 | 691 | return PTR_ERR_OR_ZERO(*hw); |
---|
680 | 692 | } |
---|
681 | 693 | |
---|
| 694 | +static int __maybe_unused armada_3700_periph_clock_suspend(struct device *dev) |
---|
| 695 | +{ |
---|
| 696 | + struct clk_periph_driver_data *data = dev_get_drvdata(dev); |
---|
| 697 | + |
---|
| 698 | + data->tbg_sel = readl(data->reg + TBG_SEL); |
---|
| 699 | + data->div_sel0 = readl(data->reg + DIV_SEL0); |
---|
| 700 | + data->div_sel1 = readl(data->reg + DIV_SEL1); |
---|
| 701 | + data->div_sel2 = readl(data->reg + DIV_SEL2); |
---|
| 702 | + data->clk_sel = readl(data->reg + CLK_SEL); |
---|
| 703 | + data->clk_dis = readl(data->reg + CLK_DIS); |
---|
| 704 | + |
---|
| 705 | + return 0; |
---|
| 706 | +} |
---|
| 707 | + |
---|
| 708 | +static int __maybe_unused armada_3700_periph_clock_resume(struct device *dev) |
---|
| 709 | +{ |
---|
| 710 | + struct clk_periph_driver_data *data = dev_get_drvdata(dev); |
---|
| 711 | + |
---|
| 712 | + /* Follow the same order than what the Cortex-M3 does (ATF code) */ |
---|
| 713 | + writel(data->clk_dis, data->reg + CLK_DIS); |
---|
| 714 | + writel(data->div_sel0, data->reg + DIV_SEL0); |
---|
| 715 | + writel(data->div_sel1, data->reg + DIV_SEL1); |
---|
| 716 | + writel(data->div_sel2, data->reg + DIV_SEL2); |
---|
| 717 | + writel(data->tbg_sel, data->reg + TBG_SEL); |
---|
| 718 | + writel(data->clk_sel, data->reg + CLK_SEL); |
---|
| 719 | + |
---|
| 720 | + return 0; |
---|
| 721 | +} |
---|
| 722 | + |
---|
| 723 | +static const struct dev_pm_ops armada_3700_periph_clock_pm_ops = { |
---|
| 724 | + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(armada_3700_periph_clock_suspend, |
---|
| 725 | + armada_3700_periph_clock_resume) |
---|
| 726 | +}; |
---|
| 727 | + |
---|
682 | 728 | static int armada_3700_periph_clock_probe(struct platform_device *pdev) |
---|
683 | 729 | { |
---|
684 | 730 | struct clk_periph_driver_data *driver_data; |
---|
.. | .. |
---|
687 | 733 | struct device *dev = &pdev->dev; |
---|
688 | 734 | int num_periph = 0, i, ret; |
---|
689 | 735 | struct resource *res; |
---|
690 | | - void __iomem *reg; |
---|
691 | 736 | |
---|
692 | 737 | data = of_device_get_match_data(dev); |
---|
693 | 738 | if (!data) |
---|
.. | .. |
---|
695 | 740 | |
---|
696 | 741 | while (data[num_periph].name) |
---|
697 | 742 | num_periph++; |
---|
698 | | - |
---|
699 | | - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
---|
700 | | - reg = devm_ioremap_resource(dev, res); |
---|
701 | | - if (IS_ERR(reg)) |
---|
702 | | - return PTR_ERR(reg); |
---|
703 | 743 | |
---|
704 | 744 | driver_data = devm_kzalloc(dev, sizeof(*driver_data), GFP_KERNEL); |
---|
705 | 745 | if (!driver_data) |
---|
.. | .. |
---|
713 | 753 | return -ENOMEM; |
---|
714 | 754 | driver_data->hw_data->num = num_periph; |
---|
715 | 755 | |
---|
| 756 | + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
---|
| 757 | + driver_data->reg = devm_ioremap_resource(dev, res); |
---|
| 758 | + if (IS_ERR(driver_data->reg)) |
---|
| 759 | + return PTR_ERR(driver_data->reg); |
---|
| 760 | + |
---|
716 | 761 | spin_lock_init(&driver_data->lock); |
---|
717 | 762 | |
---|
718 | 763 | for (i = 0; i < num_periph; i++) { |
---|
719 | 764 | struct clk_hw **hw = &driver_data->hw_data->hws[i]; |
---|
720 | | - |
---|
721 | | - if (armada_3700_add_composite_clk(&data[i], reg, |
---|
| 765 | + if (armada_3700_add_composite_clk(&data[i], driver_data->reg, |
---|
722 | 766 | &driver_data->lock, dev, hw)) |
---|
723 | 767 | dev_err(dev, "Can't register periph clock %s\n", |
---|
724 | 768 | data[i].name); |
---|
.. | .. |
---|
756 | 800 | .driver = { |
---|
757 | 801 | .name = "marvell-armada-3700-periph-clock", |
---|
758 | 802 | .of_match_table = armada_3700_periph_clock_of_match, |
---|
| 803 | + .pm = &armada_3700_periph_clock_pm_ops, |
---|
759 | 804 | }, |
---|
760 | 805 | }; |
---|
761 | 806 | |
---|