Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at master 177 lines 4.3 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Freescale SAI BCLK as a generic clock driver 4 * 5 * Copyright 2020 Michael Walle <michael@walle.cc> 6 */ 7 8#include <linux/clk-provider.h> 9#include <linux/clk.h> 10#include <linux/err.h> 11#include <linux/module.h> 12#include <linux/of.h> 13#include <linux/of_address.h> 14#include <linux/platform_device.h> 15#include <linux/slab.h> 16 17#define I2S_CSR 0x00 18#define I2S_CR2 0x08 19#define I2S_MCR 0x100 20#define CSR_BCE_BIT 28 21#define CSR_TE_BIT 31 22#define CR2_BCD BIT(24) 23#define CR2_DIV_SHIFT 0 24#define CR2_DIV_WIDTH 8 25#define MCR_MOE BIT(30) 26 27struct fsl_sai_data { 28 unsigned int offset; /* Register offset */ 29 bool have_mclk; /* Have MCLK control */ 30}; 31 32struct fsl_sai_clk { 33 const struct fsl_sai_data *data; 34 struct clk_divider bclk_div; 35 struct clk_divider mclk_div; 36 struct clk_gate bclk_gate; 37 struct clk_gate mclk_gate; 38 struct clk_hw *bclk_hw; 39 struct clk_hw *mclk_hw; 40 spinlock_t lock; 41}; 42 43static struct clk_hw * 44fsl_sai_of_clk_get(struct of_phandle_args *clkspec, void *data) 45{ 46 struct fsl_sai_clk *sai_clk = data; 47 48 if (clkspec->args_count == 0) 49 return sai_clk->bclk_hw; 50 51 if (clkspec->args_count == 1) { 52 if (clkspec->args[0] == 0) 53 return sai_clk->bclk_hw; 54 if (sai_clk->data->have_mclk && clkspec->args[0] == 1) 55 return sai_clk->mclk_hw; 56 } 57 58 return ERR_PTR(-EINVAL); 59} 60 61static int fsl_sai_clk_register(struct device *dev, void __iomem *base, 62 spinlock_t *lock, struct clk_divider *div, 63 struct clk_gate *gate, struct clk_hw **hw, 64 const int gate_bit, const int dir_bit, 65 const int div_reg, char *name) 66{ 67 const struct fsl_sai_data *data = device_get_match_data(dev); 68 struct clk_parent_data pdata = { .index = 0 }; 69 struct clk_hw *chw; 70 char *cname; 71 72 gate->reg = base + data->offset + I2S_CSR; 73 gate->bit_idx = gate_bit; 74 gate->lock = lock; 75 76 div->reg = base + div_reg; 77 div->shift = CR2_DIV_SHIFT; 78 div->width = CR2_DIV_WIDTH; 79 div->lock = lock; 80 81 cname = devm_kasprintf(dev, GFP_KERNEL, "%s.%s", 82 of_node_full_name(dev->of_node), name); 83 if (!cname) 84 return -ENOMEM; 85 86 /* Set clock direction */ 87 writel(dir_bit, base + div_reg); 88 89 chw = devm_clk_hw_register_composite_pdata(dev, cname, 90 &pdata, 1, NULL, NULL, 91 &div->hw, 92 &clk_divider_ops, 93 &gate->hw, 94 &clk_gate_ops, 95 CLK_SET_RATE_GATE); 96 if (IS_ERR(chw)) 97 return PTR_ERR(chw); 98 99 *hw = chw; 100 101 return 0; 102} 103 104static int fsl_sai_clk_probe(struct platform_device *pdev) 105{ 106 struct device *dev = &pdev->dev; 107 const struct fsl_sai_data *data = device_get_match_data(dev); 108 struct fsl_sai_clk *sai_clk; 109 struct clk *clk_bus; 110 void __iomem *base; 111 int ret; 112 113 sai_clk = devm_kzalloc(dev, sizeof(*sai_clk), GFP_KERNEL); 114 if (!sai_clk) 115 return -ENOMEM; 116 117 base = devm_platform_ioremap_resource(pdev, 0); 118 if (IS_ERR(base)) 119 return PTR_ERR(base); 120 121 clk_bus = devm_clk_get_optional_enabled(dev, "bus"); 122 if (IS_ERR(clk_bus)) 123 return PTR_ERR(clk_bus); 124 125 sai_clk->data = data; 126 spin_lock_init(&sai_clk->lock); 127 128 ret = fsl_sai_clk_register(dev, base, &sai_clk->lock, 129 &sai_clk->bclk_div, &sai_clk->bclk_gate, 130 &sai_clk->bclk_hw, CSR_BCE_BIT, CR2_BCD, 131 data->offset + I2S_CR2, "BCLK"); 132 if (ret) 133 return ret; 134 135 if (data->have_mclk) { 136 ret = fsl_sai_clk_register(dev, base, &sai_clk->lock, 137 &sai_clk->mclk_div, 138 &sai_clk->mclk_gate, 139 &sai_clk->mclk_hw, 140 CSR_TE_BIT, MCR_MOE, I2S_MCR, 141 "MCLK"); 142 if (ret) 143 return ret; 144 } 145 146 return devm_of_clk_add_hw_provider(dev, fsl_sai_of_clk_get, sai_clk); 147} 148 149static const struct fsl_sai_data fsl_sai_vf610_data = { 150 .offset = 0, 151 .have_mclk = false, 152}; 153 154static const struct fsl_sai_data fsl_sai_imx8mq_data = { 155 .offset = 8, 156 .have_mclk = true, 157}; 158 159static const struct of_device_id of_fsl_sai_clk_ids[] = { 160 { .compatible = "fsl,vf610-sai-clock", .data = &fsl_sai_vf610_data }, 161 { .compatible = "fsl,imx8mq-sai-clock", .data = &fsl_sai_imx8mq_data }, 162 { } 163}; 164MODULE_DEVICE_TABLE(of, of_fsl_sai_clk_ids); 165 166static struct platform_driver fsl_sai_clk_driver = { 167 .probe = fsl_sai_clk_probe, 168 .driver = { 169 .name = "fsl-sai-clk", 170 .of_match_table = of_fsl_sai_clk_ids, 171 }, 172}; 173module_platform_driver(fsl_sai_clk_driver); 174 175MODULE_DESCRIPTION("Freescale SAI bitclock-as-a-clock driver"); 176MODULE_AUTHOR("Michael Walle <michael@walle.cc>"); 177MODULE_ALIAS("platform:fsl-sai-clk");