| // SPDX-License-Identifier: GPL-2.0 | 
| /* | 
|  * Freescale SAI BCLK as a generic clock driver | 
|  * | 
|  * Copyright 2020 Michael Walle <michael@walle.cc> | 
|  */ | 
|   | 
| #include <linux/module.h> | 
| #include <linux/platform_device.h> | 
| #include <linux/clk-provider.h> | 
| #include <linux/err.h> | 
| #include <linux/of.h> | 
| #include <linux/of_address.h> | 
| #include <linux/slab.h> | 
|   | 
| #define I2S_CSR        0x00 | 
| #define I2S_CR2        0x08 | 
| #define CSR_BCE_BIT    28 | 
| #define CR2_BCD        BIT(24) | 
| #define CR2_DIV_SHIFT    0 | 
| #define CR2_DIV_WIDTH    8 | 
|   | 
| struct fsl_sai_clk { | 
|     struct clk_divider div; | 
|     struct clk_gate gate; | 
|     spinlock_t lock; | 
| }; | 
|   | 
| static int fsl_sai_clk_probe(struct platform_device *pdev) | 
| { | 
|     struct device *dev = &pdev->dev; | 
|     struct fsl_sai_clk *sai_clk; | 
|     struct clk_parent_data pdata = { .index = 0 }; | 
|     void __iomem *base; | 
|     struct clk_hw *hw; | 
|     struct resource *res; | 
|   | 
|     sai_clk = devm_kzalloc(dev, sizeof(*sai_clk), GFP_KERNEL); | 
|     if (!sai_clk) | 
|         return -ENOMEM; | 
|   | 
|     res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
|     base = devm_ioremap_resource(dev, res); | 
|     if (IS_ERR(base)) | 
|         return PTR_ERR(base); | 
|   | 
|     spin_lock_init(&sai_clk->lock); | 
|   | 
|     sai_clk->gate.reg = base + I2S_CSR; | 
|     sai_clk->gate.bit_idx = CSR_BCE_BIT; | 
|     sai_clk->gate.lock = &sai_clk->lock; | 
|   | 
|     sai_clk->div.reg = base + I2S_CR2; | 
|     sai_clk->div.shift = CR2_DIV_SHIFT; | 
|     sai_clk->div.width = CR2_DIV_WIDTH; | 
|     sai_clk->div.lock = &sai_clk->lock; | 
|   | 
|     /* set clock direction, we are the BCLK master */ | 
|     writel(CR2_BCD, base + I2S_CR2); | 
|   | 
|     hw = clk_hw_register_composite_pdata(dev, dev->of_node->name, | 
|                          &pdata, 1, NULL, NULL, | 
|                          &sai_clk->div.hw, | 
|                          &clk_divider_ops, | 
|                          &sai_clk->gate.hw, | 
|                          &clk_gate_ops, | 
|                          CLK_SET_RATE_GATE); | 
|     if (IS_ERR(hw)) | 
|         return PTR_ERR(hw); | 
|   | 
|     platform_set_drvdata(pdev, hw); | 
|   | 
|     return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw); | 
| } | 
|   | 
| static int fsl_sai_clk_remove(struct platform_device *pdev) | 
| { | 
|     struct clk_hw *hw = platform_get_drvdata(pdev); | 
|   | 
|     clk_hw_unregister_composite(hw); | 
|   | 
|     return 0; | 
| } | 
|   | 
| static const struct of_device_id of_fsl_sai_clk_ids[] = { | 
|     { .compatible = "fsl,vf610-sai-clock" }, | 
|     { } | 
| }; | 
| MODULE_DEVICE_TABLE(of, of_fsl_sai_clk_ids); | 
|   | 
| static struct platform_driver fsl_sai_clk_driver = { | 
|     .probe = fsl_sai_clk_probe, | 
|     .remove = fsl_sai_clk_remove, | 
|     .driver        = { | 
|         .name    = "fsl-sai-clk", | 
|         .of_match_table = of_fsl_sai_clk_ids, | 
|     }, | 
| }; | 
| module_platform_driver(fsl_sai_clk_driver); | 
|   | 
| MODULE_DESCRIPTION("Freescale SAI bitclock-as-a-clock driver"); | 
| MODULE_AUTHOR("Michael Walle <michael@walle.cc>"); | 
| MODULE_LICENSE("GPL"); | 
| MODULE_ALIAS("platform:fsl-sai-clk"); |