diff options
| -rw-r--r-- | arch/arm/mach-at91/Kconfig | 4 | ||||
| -rw-r--r-- | drivers/clk/at91/Makefile | 1 | ||||
| -rw-r--r-- | drivers/clk/at91/clk-i2s-mux.c | 116 |
3 files changed, 121 insertions, 0 deletions
diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig index 1254bf9d91b4..903f23c309df 100644 --- a/arch/arm/mach-at91/Kconfig +++ b/arch/arm/mach-at91/Kconfig | |||
| @@ -27,6 +27,7 @@ config SOC_SAMA5D2 | |||
| 27 | select HAVE_AT91_H32MX | 27 | select HAVE_AT91_H32MX |
| 28 | select HAVE_AT91_GENERATED_CLK | 28 | select HAVE_AT91_GENERATED_CLK |
| 29 | select HAVE_AT91_AUDIO_PLL | 29 | select HAVE_AT91_AUDIO_PLL |
| 30 | select HAVE_AT91_I2S_MUX_CLK | ||
| 30 | select PINCTRL_AT91PIO4 | 31 | select PINCTRL_AT91PIO4 |
| 31 | help | 32 | help |
| 32 | Select this if ou are using one of Microchip's SAMA5D2 family SoC. | 33 | Select this if ou are using one of Microchip's SAMA5D2 family SoC. |
| @@ -129,6 +130,9 @@ config HAVE_AT91_GENERATED_CLK | |||
| 129 | config HAVE_AT91_AUDIO_PLL | 130 | config HAVE_AT91_AUDIO_PLL |
| 130 | bool | 131 | bool |
| 131 | 132 | ||
| 133 | config HAVE_AT91_I2S_MUX_CLK | ||
| 134 | bool | ||
| 135 | |||
| 132 | config SOC_SAM_V4_V5 | 136 | config SOC_SAM_V4_V5 |
| 133 | bool | 137 | bool |
| 134 | 138 | ||
diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile index 082596f37c1d..facc169ebb68 100644 --- a/drivers/clk/at91/Makefile +++ b/drivers/clk/at91/Makefile | |||
| @@ -13,3 +13,4 @@ obj-$(CONFIG_HAVE_AT91_USB_CLK) += clk-usb.o | |||
| 13 | obj-$(CONFIG_HAVE_AT91_SMD) += clk-smd.o | 13 | obj-$(CONFIG_HAVE_AT91_SMD) += clk-smd.o |
| 14 | obj-$(CONFIG_HAVE_AT91_H32MX) += clk-h32mx.o | 14 | obj-$(CONFIG_HAVE_AT91_H32MX) += clk-h32mx.o |
| 15 | obj-$(CONFIG_HAVE_AT91_GENERATED_CLK) += clk-generated.o | 15 | obj-$(CONFIG_HAVE_AT91_GENERATED_CLK) += clk-generated.o |
| 16 | obj-$(CONFIG_HAVE_AT91_I2S_MUX_CLK) += clk-i2s-mux.o | ||
diff --git a/drivers/clk/at91/clk-i2s-mux.c b/drivers/clk/at91/clk-i2s-mux.c new file mode 100644 index 000000000000..f0c3c3079f04 --- /dev/null +++ b/drivers/clk/at91/clk-i2s-mux.c | |||
| @@ -0,0 +1,116 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * Copyright (C) 2018 Microchip Technology Inc, | ||
| 4 | * Codrin Ciubotariu <codrin.ciubotariu@microchip.com> | ||
| 5 | * | ||
| 6 | * | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/clk-provider.h> | ||
| 10 | #include <linux/of.h> | ||
| 11 | #include <linux/mfd/syscon.h> | ||
| 12 | #include <linux/regmap.h> | ||
| 13 | #include <linux/slab.h> | ||
| 14 | |||
| 15 | #include <soc/at91/atmel-sfr.h> | ||
| 16 | |||
| 17 | #define I2S_BUS_NR 2 | ||
| 18 | |||
| 19 | struct clk_i2s_mux { | ||
| 20 | struct clk_hw hw; | ||
| 21 | struct regmap *regmap; | ||
| 22 | u8 bus_id; | ||
| 23 | }; | ||
| 24 | |||
| 25 | #define to_clk_i2s_mux(hw) container_of(hw, struct clk_i2s_mux, hw) | ||
| 26 | |||
| 27 | static u8 clk_i2s_mux_get_parent(struct clk_hw *hw) | ||
| 28 | { | ||
| 29 | struct clk_i2s_mux *mux = to_clk_i2s_mux(hw); | ||
| 30 | u32 val; | ||
| 31 | |||
| 32 | regmap_read(mux->regmap, AT91_SFR_I2SCLKSEL, &val); | ||
| 33 | |||
| 34 | return (val & BIT(mux->bus_id)) >> mux->bus_id; | ||
| 35 | } | ||
| 36 | |||
| 37 | static int clk_i2s_mux_set_parent(struct clk_hw *hw, u8 index) | ||
| 38 | { | ||
| 39 | struct clk_i2s_mux *mux = to_clk_i2s_mux(hw); | ||
| 40 | |||
| 41 | return regmap_update_bits(mux->regmap, AT91_SFR_I2SCLKSEL, | ||
| 42 | BIT(mux->bus_id), index << mux->bus_id); | ||
| 43 | } | ||
| 44 | |||
| 45 | static const struct clk_ops clk_i2s_mux_ops = { | ||
| 46 | .get_parent = clk_i2s_mux_get_parent, | ||
| 47 | .set_parent = clk_i2s_mux_set_parent, | ||
| 48 | .determine_rate = __clk_mux_determine_rate, | ||
| 49 | }; | ||
| 50 | |||
| 51 | static struct clk_hw * __init | ||
| 52 | at91_clk_i2s_mux_register(struct regmap *regmap, const char *name, | ||
| 53 | const char * const *parent_names, | ||
| 54 | unsigned int num_parents, u8 bus_id) | ||
| 55 | { | ||
| 56 | struct clk_init_data init = {}; | ||
| 57 | struct clk_i2s_mux *i2s_ck; | ||
| 58 | int ret; | ||
| 59 | |||
| 60 | i2s_ck = kzalloc(sizeof(*i2s_ck), GFP_KERNEL); | ||
| 61 | if (!i2s_ck) | ||
| 62 | return ERR_PTR(-ENOMEM); | ||
| 63 | |||
| 64 | init.name = name; | ||
| 65 | init.ops = &clk_i2s_mux_ops; | ||
| 66 | init.parent_names = parent_names; | ||
| 67 | init.num_parents = num_parents; | ||
| 68 | |||
| 69 | i2s_ck->hw.init = &init; | ||
| 70 | i2s_ck->bus_id = bus_id; | ||
| 71 | i2s_ck->regmap = regmap; | ||
| 72 | |||
| 73 | ret = clk_hw_register(NULL, &i2s_ck->hw); | ||
| 74 | if (ret) { | ||
| 75 | kfree(i2s_ck); | ||
| 76 | return ERR_PTR(ret); | ||
| 77 | } | ||
| 78 | |||
| 79 | return &i2s_ck->hw; | ||
| 80 | } | ||
| 81 | |||
| 82 | static void __init of_sama5d2_clk_i2s_mux_setup(struct device_node *np) | ||
| 83 | { | ||
| 84 | struct regmap *regmap_sfr; | ||
| 85 | u8 bus_id; | ||
| 86 | const char *parent_names[2]; | ||
| 87 | struct device_node *i2s_mux_np; | ||
| 88 | struct clk_hw *hw; | ||
| 89 | int ret; | ||
| 90 | |||
| 91 | regmap_sfr = syscon_regmap_lookup_by_compatible("atmel,sama5d2-sfr"); | ||
| 92 | if (IS_ERR(regmap_sfr)) | ||
| 93 | return; | ||
| 94 | |||
| 95 | for_each_child_of_node(np, i2s_mux_np) { | ||
| 96 | if (of_property_read_u8(i2s_mux_np, "reg", &bus_id)) | ||
| 97 | continue; | ||
| 98 | |||
| 99 | if (bus_id > I2S_BUS_NR) | ||
| 100 | continue; | ||
| 101 | |||
| 102 | ret = of_clk_parent_fill(i2s_mux_np, parent_names, 2); | ||
| 103 | if (ret != 2) | ||
| 104 | continue; | ||
| 105 | |||
| 106 | hw = at91_clk_i2s_mux_register(regmap_sfr, i2s_mux_np->name, | ||
| 107 | parent_names, 2, bus_id); | ||
| 108 | if (IS_ERR(hw)) | ||
| 109 | continue; | ||
| 110 | |||
| 111 | of_clk_add_hw_provider(i2s_mux_np, of_clk_hw_simple_get, hw); | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | CLK_OF_DECLARE(sama5d2_clk_i2s_mux, "atmel,sama5d2-clk-i2s-mux", | ||
| 116 | of_sama5d2_clk_i2s_mux_setup); | ||
