196e4ea8cSCodrin Ciubotariu // SPDX-License-Identifier: GPL-2.0 296e4ea8cSCodrin Ciubotariu /* 396e4ea8cSCodrin Ciubotariu * Copyright (C) 2018 Microchip Technology Inc, 496e4ea8cSCodrin Ciubotariu * Codrin Ciubotariu <codrin.ciubotariu@microchip.com> 596e4ea8cSCodrin Ciubotariu * 696e4ea8cSCodrin Ciubotariu * 796e4ea8cSCodrin Ciubotariu */ 896e4ea8cSCodrin Ciubotariu 996e4ea8cSCodrin Ciubotariu #include <linux/clk-provider.h> 1096e4ea8cSCodrin Ciubotariu #include <linux/of.h> 1196e4ea8cSCodrin Ciubotariu #include <linux/mfd/syscon.h> 1296e4ea8cSCodrin Ciubotariu #include <linux/regmap.h> 1396e4ea8cSCodrin Ciubotariu #include <linux/slab.h> 1496e4ea8cSCodrin Ciubotariu 1596e4ea8cSCodrin Ciubotariu #include <soc/at91/atmel-sfr.h> 1696e4ea8cSCodrin Ciubotariu 1796e4ea8cSCodrin Ciubotariu #define I2S_BUS_NR 2 1896e4ea8cSCodrin Ciubotariu 1996e4ea8cSCodrin Ciubotariu struct clk_i2s_mux { 2096e4ea8cSCodrin Ciubotariu struct clk_hw hw; 2196e4ea8cSCodrin Ciubotariu struct regmap *regmap; 2296e4ea8cSCodrin Ciubotariu u8 bus_id; 2396e4ea8cSCodrin Ciubotariu }; 2496e4ea8cSCodrin Ciubotariu 2596e4ea8cSCodrin Ciubotariu #define to_clk_i2s_mux(hw) container_of(hw, struct clk_i2s_mux, hw) 2696e4ea8cSCodrin Ciubotariu 2796e4ea8cSCodrin Ciubotariu static u8 clk_i2s_mux_get_parent(struct clk_hw *hw) 2896e4ea8cSCodrin Ciubotariu { 2996e4ea8cSCodrin Ciubotariu struct clk_i2s_mux *mux = to_clk_i2s_mux(hw); 3096e4ea8cSCodrin Ciubotariu u32 val; 3196e4ea8cSCodrin Ciubotariu 3296e4ea8cSCodrin Ciubotariu regmap_read(mux->regmap, AT91_SFR_I2SCLKSEL, &val); 3396e4ea8cSCodrin Ciubotariu 3496e4ea8cSCodrin Ciubotariu return (val & BIT(mux->bus_id)) >> mux->bus_id; 3596e4ea8cSCodrin Ciubotariu } 3696e4ea8cSCodrin Ciubotariu 3796e4ea8cSCodrin Ciubotariu static int clk_i2s_mux_set_parent(struct clk_hw *hw, u8 index) 3896e4ea8cSCodrin Ciubotariu { 3996e4ea8cSCodrin Ciubotariu struct clk_i2s_mux *mux = to_clk_i2s_mux(hw); 4096e4ea8cSCodrin Ciubotariu 4196e4ea8cSCodrin Ciubotariu return regmap_update_bits(mux->regmap, AT91_SFR_I2SCLKSEL, 4296e4ea8cSCodrin Ciubotariu BIT(mux->bus_id), index << mux->bus_id); 4396e4ea8cSCodrin Ciubotariu } 4496e4ea8cSCodrin Ciubotariu 4596e4ea8cSCodrin Ciubotariu static const struct clk_ops clk_i2s_mux_ops = { 4696e4ea8cSCodrin Ciubotariu .get_parent = clk_i2s_mux_get_parent, 4796e4ea8cSCodrin Ciubotariu .set_parent = clk_i2s_mux_set_parent, 4896e4ea8cSCodrin Ciubotariu .determine_rate = __clk_mux_determine_rate, 4996e4ea8cSCodrin Ciubotariu }; 5096e4ea8cSCodrin Ciubotariu 5196e4ea8cSCodrin Ciubotariu static struct clk_hw * __init 5296e4ea8cSCodrin Ciubotariu at91_clk_i2s_mux_register(struct regmap *regmap, const char *name, 5396e4ea8cSCodrin Ciubotariu const char * const *parent_names, 5496e4ea8cSCodrin Ciubotariu unsigned int num_parents, u8 bus_id) 5596e4ea8cSCodrin Ciubotariu { 5696e4ea8cSCodrin Ciubotariu struct clk_init_data init = {}; 5796e4ea8cSCodrin Ciubotariu struct clk_i2s_mux *i2s_ck; 5896e4ea8cSCodrin Ciubotariu int ret; 5996e4ea8cSCodrin Ciubotariu 6096e4ea8cSCodrin Ciubotariu i2s_ck = kzalloc(sizeof(*i2s_ck), GFP_KERNEL); 6196e4ea8cSCodrin Ciubotariu if (!i2s_ck) 6296e4ea8cSCodrin Ciubotariu return ERR_PTR(-ENOMEM); 6396e4ea8cSCodrin Ciubotariu 6496e4ea8cSCodrin Ciubotariu init.name = name; 6596e4ea8cSCodrin Ciubotariu init.ops = &clk_i2s_mux_ops; 6696e4ea8cSCodrin Ciubotariu init.parent_names = parent_names; 6796e4ea8cSCodrin Ciubotariu init.num_parents = num_parents; 6896e4ea8cSCodrin Ciubotariu 6996e4ea8cSCodrin Ciubotariu i2s_ck->hw.init = &init; 7096e4ea8cSCodrin Ciubotariu i2s_ck->bus_id = bus_id; 7196e4ea8cSCodrin Ciubotariu i2s_ck->regmap = regmap; 7296e4ea8cSCodrin Ciubotariu 7396e4ea8cSCodrin Ciubotariu ret = clk_hw_register(NULL, &i2s_ck->hw); 7496e4ea8cSCodrin Ciubotariu if (ret) { 7596e4ea8cSCodrin Ciubotariu kfree(i2s_ck); 7696e4ea8cSCodrin Ciubotariu return ERR_PTR(ret); 7796e4ea8cSCodrin Ciubotariu } 7896e4ea8cSCodrin Ciubotariu 7996e4ea8cSCodrin Ciubotariu return &i2s_ck->hw; 8096e4ea8cSCodrin Ciubotariu } 8196e4ea8cSCodrin Ciubotariu 8296e4ea8cSCodrin Ciubotariu static void __init of_sama5d2_clk_i2s_mux_setup(struct device_node *np) 8396e4ea8cSCodrin Ciubotariu { 8496e4ea8cSCodrin Ciubotariu struct regmap *regmap_sfr; 8596e4ea8cSCodrin Ciubotariu u8 bus_id; 8696e4ea8cSCodrin Ciubotariu const char *parent_names[2]; 8796e4ea8cSCodrin Ciubotariu struct device_node *i2s_mux_np; 8896e4ea8cSCodrin Ciubotariu struct clk_hw *hw; 8996e4ea8cSCodrin Ciubotariu int ret; 9096e4ea8cSCodrin Ciubotariu 9196e4ea8cSCodrin Ciubotariu regmap_sfr = syscon_regmap_lookup_by_compatible("atmel,sama5d2-sfr"); 9296e4ea8cSCodrin Ciubotariu if (IS_ERR(regmap_sfr)) 9396e4ea8cSCodrin Ciubotariu return; 9496e4ea8cSCodrin Ciubotariu 9596e4ea8cSCodrin Ciubotariu for_each_child_of_node(np, i2s_mux_np) { 9696e4ea8cSCodrin Ciubotariu if (of_property_read_u8(i2s_mux_np, "reg", &bus_id)) 9796e4ea8cSCodrin Ciubotariu continue; 9896e4ea8cSCodrin Ciubotariu 9996e4ea8cSCodrin Ciubotariu if (bus_id > I2S_BUS_NR) 10096e4ea8cSCodrin Ciubotariu continue; 10196e4ea8cSCodrin Ciubotariu 10296e4ea8cSCodrin Ciubotariu ret = of_clk_parent_fill(i2s_mux_np, parent_names, 2); 10396e4ea8cSCodrin Ciubotariu if (ret != 2) 10496e4ea8cSCodrin Ciubotariu continue; 10596e4ea8cSCodrin Ciubotariu 10696e4ea8cSCodrin Ciubotariu hw = at91_clk_i2s_mux_register(regmap_sfr, i2s_mux_np->name, 10796e4ea8cSCodrin Ciubotariu parent_names, 2, bus_id); 10896e4ea8cSCodrin Ciubotariu if (IS_ERR(hw)) 10996e4ea8cSCodrin Ciubotariu continue; 11096e4ea8cSCodrin Ciubotariu 11196e4ea8cSCodrin Ciubotariu of_clk_add_hw_provider(i2s_mux_np, of_clk_hw_simple_get, hw); 11296e4ea8cSCodrin Ciubotariu } 11396e4ea8cSCodrin Ciubotariu } 11496e4ea8cSCodrin Ciubotariu 11596e4ea8cSCodrin Ciubotariu CLK_OF_DECLARE(sama5d2_clk_i2s_mux, "atmel,sama5d2-clk-i2s-mux", 11696e4ea8cSCodrin Ciubotariu of_sama5d2_clk_i2s_mux_setup); 117