/* * Clock implementation for MediaTek MT76x8 * Copyright (C) 2018 Qin Wei * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include #define MHz 1000000 #define CLKCFG0_REFCLK0_RATE 0x2c struct mt76x8_clock { struct device *dev; struct clk_hw hw; void __iomem *base; }; static int mt76x8_clock_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { unsigned long supported_rate[] = {0, 12*MHz, 25*MHz, 40*MHz, 48*MHz}; struct mt76x8_clock *clk = container_of(hw, struct mt76x8_clock, hw); u32 i, reg, clkreg; for (i = 0; i < ARRAY_SIZE(supported_rate); i++) { if (supported_rate[i] == rate) break; } if (i == ARRAY_SIZE(supported_rate)) return -EERROR; // no supported rate. switch (i) { case 0: reg = 6; break; case 1: reg = 1; break; case 2: reg = 2; break; case 3: reg = 3; break; case 4: reg = 4; break; } /* set up register. */ clkreg = clk_readl(clk->base); clkreg = clkreg & 0xfffff1ff | (reg << 9); clk_writel(clkreg, clk->base); return 0; } static const struct clk_ops mt76x8_clock_ops = { .set_rate = mt76x8_clock_set_rate, } static struct clk_init_data mt76x8_clock_init = { .name = "mt76x8-clock", .ops = &mt76x8_clock_ops, .flags = CLK_GET_RATE_NOCACHE, }; static int mt76x8_clock_probe(struct platform_device *pdev) { struct mt76x8_clock *clkdata; struct resource *res; int ret; clkdata = devm_kzalloc(&pdev->dev, sizeof(*clkdata), GFP_KERNEL); if (!clkdata) return -ENOMEM; clkdata->dev = &pdev->dev; clkdata->hw.init = &mt76x8_clock_init; ret = devm_clk_hw_register(&pdev->dev, &clkdata->hw); if (ret) return ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); clkdata->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(clkdata->base)) return PTR_ERR(clkdata->base); clkdata->base += CLKCFG0_REFCLK0_RATE; platform_set_drvdata(pdev, clkdata); return of_clk_add_hw_provider(pdev->dev.parent->of_node, of_clk_hw_simple_get, &clkdata->hw); } static struct platform_driver mt76x8_clock_driver = { .driver = { .name = "ralink,rt2880-clock", }, .probe = mt76x8_clock_probe, }; module_platform_driver(mt76x8_clock_driver); MODULE_AUTHOR("Qin Wei "); MODULE_DESCRIPTION("mt76x8 clock driver"); MODULE_LICENSE("GPL");