Skip to content
Snippets Groups Projects
Commit db06f826 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull clk updates from Stephen Boyd:
 "The new and exciting feature this time around is in the clk core.
  We've added duty cycle support to the clk API so that clk signal duty
  cycle ratios can be adjusted while taking into account things like clk
  dividers and clk tree hierarchy. So far only one SoC has implemented
  support for this, but I expect there will be more to come in the
  future.

  Outside of the core, we have the usual pile of clk driver updates and
  additions. The Amlogic meson driver got the most lines in the diffstat
  this time around because it added support for a whole bunch of
  hardware and duty cycle configuration. After that the Rockchip PX30,
  Qualcomm SDM845, and Renesas SoC drivers fill in a majority of the
  diff. We're left with the collection of non-critical fixes after that.
  Overall it looks pretty quiet this time.

  Core:
   - Clk duty cycle support
   - Proper CLK_SET_RATE_GATE support throughout the tree

  New Drivers:
   - Actions Semi Owl series S700 SoC clk driver
   - Qualcomm SDM845 display clock controller
   - i.MX6SX ocram_s clk support
   - Uniphier NAND, USB3 PHY, and SPI clk support
   - Qualcomm RPMh clk driver
   - i.MX7D mailbox clk support
   - Maxim 9485 Programmable Clock Generator
   - expose 32 kHz PLL on PXA SoCs
   - imx6sll GPIO clk gate support
   - Atmel at91 I2S audio clk support
   - SI544/SI514 clk on/off support
   - i.MX6UL GPIO clock gates in CCM CCGR
   - Renesas Crypto Engine clocks on R-Car H3
   - Renesas clk support for the new RZ/N1D SoC
   - Allwinner A64 display engine clock support
   - support for Rockchip's PX30 SoC
   - Amlogic Meson axg PCIe and audio clocks
   - Amlogic Meson GEN CLK on gxbb, gxl and axg

  Updates:
   - remove an unused variable from Exynos4412 ISP driver
   - fix a thinko bug in SCMI clk division logic
   - add missing of_node_put()s in some i.MX clk drivers
   - Tegra SDMMC clk jitter improvements with high speed signaling modes
   - SPDX tagging for qcom and cs2000-cp drivers
   - stop leaking con ids in __clk_put()
   - fix a corner case in fixed factor clk probing where node is in DT
     but parent clk is registered much later
   - Marvell Armada 3700 clk_pm_cpu_get_parent() had an invalid return
     value
   - i.MX clk init arrays removed in place of CLK_IS_CRITICAL
   - convert to CLK_IS_CRITICAL for i.MX51/53 driver
   - fix Tegra BPMP driver oops when xlating a NULL clk
   - proper default configuration for vic03 and vde clks on Tegra124
   - mark Tegra memory controller clks as critical
   - fix array bounds clamp in Tegra's emc determine_rate() op
   - Ingenic i2s bit update and allow UDC clk to gate
   - fix name of aspeed SDC clk define to have only one 'CLK'
   - fix i.MX6QDL video clk parent
   - critical clk markings for qcom SDM845
   - fix Stratix10 mpu_free_clk and sdmmc_free_clk parents
   - mark Rockchip's pclk_rkpwm_pmu as critical clock, due to it
     supplying the pwm used to drive the logic supply of the rk3399
     core"

* tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux: (85 commits)
  clk: rockchip: Add pclk_rkpwm_pmu to PMU critical clocks in rk3399
  clk: cs2000-cp: convert to SPDX identifiers
  clk: scmi: Fix the rounding of clock rate
  clk: qcom: Add display clock controller driver for SDM845
  clk: mvebu: armada-37xx-periph: Remove unused var num_parents
  clk: samsung: Remove unused mout_user_aclk400_mcuisp_p4x12 variable
  clk: actions: Add S700 SoC clock support
  dt-bindings: clock: Add S700 support for Actions Semi Soc's
  clk: actions: Add missing REGMAP_MMIO dependency
  clk: uniphier: add clock frequency support for SPI
  clk: uniphier: add more USB3 PHY clocks
  clk: uniphier: add NAND 200MHz clock
  clk: tegra: make sdmmc2 and sdmmc4 as sdmmc clocks
  clk: tegra: Add sdmmc mux divider clock
  clk: tegra: Refactor fractional divider calculation
  clk: tegra: Fix includes required by fence_udelay()
  clk: imx6sll: fix missing of_node_put()
  clk: imx6ul: fix missing of_node_put()
  clk: imx: add ocram_s clock for i.mx6sx
  clk: mvebu: armada-37xx-periph: Fix wrong return value in get_parent
  ...
parents 6de4c691 ac7da1b7
No related branches found
No related tags found
No related merge requests found
Showing
with 1428 additions and 15 deletions
* Actions S900 Clock Management Unit (CMU)
* Actions Semi Owl Clock Management Unit (CMU)
The Actions S900 clock management unit generates and supplies clock to various
controllers within the SoC. The clock binding described here is applicable to
S900 SoC.
The Actions Semi Owl Clock Management Unit generates and supplies clock
to various controllers within the SoC. The clock binding described here is
applicable to S900 and S700 SoC's.
Required Properties:
- compatible: should be "actions,s900-cmu"
- compatible: should be one of the following,
"actions,s900-cmu"
"actions,s700-cmu"
- reg: physical base address of the controller and length of memory mapped
region.
- clocks: Reference to the parent clocks ("hosc", "losc")
......@@ -15,16 +17,16 @@ Required Properties:
Each clock is assigned an identifier, and client nodes can use this identifier
to specify the clock which they consume.
All available clocks are defined as preprocessor macros in
dt-bindings/clock/actions,s900-cmu.h header and can be used in device
tree sources.
All available clocks are defined as preprocessor macros in corresponding
dt-bindings/clock/actions,s900-cmu.h or actions,s700-cmu.h header and can be
used in device tree sources.
External clocks:
The hosc clock used as input for the plls is generated outside the SoC. It is
expected that it is defined using standard clock bindings as "hosc".
Actions S900 CMU also requires one more clock:
Actions Semi S900 CMU also requires one more clock:
- "losc" - internal low frequency oscillator
Example: Clock Management Unit node:
......
* Amlogic AXG Audio Clock Controllers
The Amlogic AXG audio clock controller generates and supplies clock to the
other elements of the audio subsystem, such as fifos, i2s, spdif and pdm
devices.
Required Properties:
- compatible : should be "amlogic,axg-audio-clkc" for the A113X and A113D
- reg : physical base address of the clock controller and length of
memory mapped region.
- clocks : a list of phandle + clock-specifier pairs for the clocks listed
in clock-names.
- clock-names : must contain the following:
* "pclk" - Main peripheral bus clock
may contain the following:
* "mst_in[0-7]" - 8 input plls to generate clock signals
* "slv_sclk[0-9]" - 10 slave bit clocks provided by external
components.
* "slv_lrclk[0-9]" - 10 slave sample clocks provided by external
components.
- resets : phandle of the internal reset line
- #clock-cells : should be 1.
Each clock is assigned an identifier and client nodes can use this identifier
to specify the clock which they consume. All available clocks are defined as
preprocessor macros in the dt-bindings/clock/axg-audio-clkc.h header and can be
used in device tree sources.
Example:
clkc_audio: clock-controller@0 {
compatible = "amlogic,axg-audio-clkc";
reg = <0x0 0x0 0x0 0xb4>;
#clock-cells = <1>;
clocks = <&clkc CLKID_AUDIO>,
<&clkc CLKID_MPLL0>,
<&clkc CLKID_MPLL1>,
<&clkc CLKID_MPLL2>,
<&clkc CLKID_MPLL3>,
<&clkc CLKID_HIFI_PLL>,
<&clkc CLKID_FCLK_DIV3>,
<&clkc CLKID_FCLK_DIV4>,
<&clkc CLKID_GP0_PLL>;
clock-names = "pclk",
"mst_in0",
"mst_in1",
"mst_in2",
"mst_in3",
"mst_in4",
"mst_in5",
"mst_in6",
"mst_in7";
resets = <&reset RESET_AUDIO>;
};
......@@ -91,6 +91,9 @@ Required properties:
at91 audio pll output on AUDIOPLLCLK that feeds the PMC
and can be used by peripheral clock or generic clock
"atmel,sama5d2-clk-i2s-mux" (under pmc node):
at91 I2S clock source selection
Required properties for SCKC node:
- reg : defines the IO memory reserved for the SCKC.
- #size-cells : shall be 0 (reg is used to encode clk id).
......@@ -500,3 +503,35 @@ For example:
atmel,clk-output-range = <0 83000000>;
};
};
Required properties for I2S mux clocks:
- #size-cells : shall be 0 (reg is used to encode I2S bus id).
- #address-cells : shall be 1 (reg is used to encode I2S bus id).
- name: device tree node describing a specific mux clock.
* #clock-cells : from common clock binding; shall be set to 0.
* clocks : shall be the mux clock parent phandles; shall be 2 phandles:
peripheral and generated clock; the first phandle shall belong to the
peripheral clock and the second one shall belong to the generated
clock; "clock-indices" property can be user to specify
the correct order.
* reg: I2S bus id of the corresponding mux clock.
e.g. reg = <0>; for i2s0, reg = <1>; for i2s1
For example:
i2s_clkmux {
compatible = "atmel,sama5d2-clk-i2s-mux";
#address-cells = <1>;
#size-cells = <0>;
i2s0muxck: i2s0_muxclk {
clocks = <&i2s0_clk>, <&i2s0_gclk>;
#clock-cells = <0>;
reg = <0>;
};
i2s1muxck: i2s1_muxclk {
clocks = <&i2s1_clk>, <&i2s1_gclk>;
#clock-cells = <0>;
reg = <1>;
};
};
Devicetree bindings for Maxim MAX9485 Programmable Audio Clock Generator
This device exposes 4 clocks in total:
- MAX9485_MCLKOUT: A gated, buffered output of the input clock of 27 MHz
- MAX9485_CLKOUT: A PLL that can be configured to 16 different discrete
frequencies
- MAX9485_CLKOUT[1,2]: Two gated outputs for MAX9485_CLKOUT
MAX9485_CLKOUT[1,2] are children of MAX9485_CLKOUT which upchain all rate set
requests.
Required properties:
- compatible: "maxim,max9485"
- clocks: Input clock, must provice 27.000 MHz
- clock-names: Must be set to "xclk"
- #clock-cells: From common clock binding; shall be set to 1
Optional properties:
- reset-gpios: GPIO descriptor connected to the #RESET input pin
- vdd-supply: A regulator node for Vdd
- clock-output-names: Name of output clocks, as defined in common clock
bindings
If not explicitly set, the output names are "mclkout", "clkout", "clkout1"
and "clkout2".
Clocks are defined as preprocessor macros in the dt-binding header.
Example:
#include <dt-bindings/clock/maxim,max9485.h>
xo-27mhz: xo-27mhz {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <27000000>;
};
&i2c0 {
max9485: audio-clock@63 {
reg = <0x63>;
compatible = "maxim,max9485";
clock-names = "xclk";
clocks = <&xo-27mhz>;
reset-gpios = <&gpio 1 GPIO_ACTIVE_HIGH>;
vdd-supply = <&3v3-reg>;
#clock-cells = <1>;
};
};
// Clock consumer node
foo@0 {
compatible = "bar,foo";
/* ... */
clock-names = "foo-input-clk";
clocks = <&max9485 MAX9485_CLKOUT1>;
};
Qualcomm Technologies, Inc. Display Clock Controller Binding
------------------------------------------------------------
Required properties :
- compatible : shall contain "qcom,sdm845-dispcc"
- reg : shall contain base register location and length.
- #clock-cells : from common clock binding, shall contain 1.
- #reset-cells : from common reset binding, shall contain 1.
- #power-domain-cells : from generic power domain binding, shall contain 1.
Example:
dispcc: clock-controller@af00000 {
compatible = "qcom,sdm845-dispcc";
reg = <0xaf00000 0x100000>;
#clock-cells = <1>;
#reset-cells = <1>;
#power-domain-cells = <1>;
};
* Renesas R9A06G032 SYSCTRL
Required Properties:
- compatible: Must be:
- "renesas,r9a06g032-sysctrl"
- reg: Base address and length of the SYSCTRL IO block.
- #clock-cells: Must be 1
- clocks: References to the parent clocks:
- external 40mhz crystal.
- external (optional) 32.768khz
- external (optional) jtag input
- external (optional) RGMII_REFCLK
- clock-names: Must be:
clock-names = "mclk", "rtc", "jtag", "rgmii_ref_ext";
Examples
--------
- SYSCTRL node:
sysctrl: system-controller@4000c000 {
compatible = "renesas,r9a06g032-sysctrl";
reg = <0x4000c000 0x1000>;
#clock-cells = <1>;
clocks = <&ext_mclk>, <&ext_rtc_clk>,
<&ext_jtag_clk>, <&ext_rgmii_ref>;
clock-names = "mclk", "rtc", "jtag", "rgmii_ref_ext";
};
- Other nodes can use the clocks provided by SYSCTRL as in:
#include <dt-bindings/clock/r9a06g032-sysctrl.h>
uart0: serial@40060000 {
compatible = "snps,dw-apb-uart";
reg = <0x40060000 0x400>;
interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
reg-shift = <2>;
reg-io-width = <4>;
clocks = <&sysctrl R9A06G032_CLK_UART0>;
clock-names = "baudclk";
};
* Rockchip PX30 Clock and Reset Unit
The PX30 clock controller generates and supplies clock to various
controllers within the SoC and also implements a reset controller for SoC
peripherals.
Required Properties:
- compatible: PMU for CRU should be "rockchip,px30-pmu-cru"
- compatible: CRU should be "rockchip,px30-cru"
- reg: physical base address of the controller and length of memory mapped
region.
- #clock-cells: should be 1.
- #reset-cells: should be 1.
Optional Properties:
- rockchip,grf: phandle to the syscon managing the "general register files"
If missing, pll rates are not changeable, due to the missing pll lock status.
Each clock is assigned an identifier and client nodes can use this identifier
to specify the clock which they consume. All available clocks are defined as
preprocessor macros in the dt-bindings/clock/px30-cru.h headers and can be
used in device tree sources. Similar macros exist for the reset sources in
these files.
External clocks:
There are several clocks that are generated outside the SoC. It is expected
that they are defined using standard clock bindings with following
clock-output-names:
- "xin24m" - crystal input - required,
- "xin32k" - rtc clock - optional,
- "i2sx_clkin" - external I2S clock - optional,
- "gmac_clkin" - external GMAC clock - optional
Example: Clock controller node:
pmucru: clock-controller@ff2bc000 {
compatible = "rockchip,px30-pmucru";
reg = <0x0 0xff2bc000 0x0 0x1000>;
#clock-cells = <1>;
#reset-cells = <1>;
};
cru: clock-controller@ff2b0000 {
compatible = "rockchip,px30-cru";
reg = <0x0 0xff2b0000 0x0 0x1000>;
rockchip,grf = <&grf>;
#clock-cells = <1>;
#reset-cells = <1>;
};
Example: UART controller node that consumes the clock generated by the clock
controller:
uart0: serial@ff030000 {
compatible = "rockchip,px30-uart", "snps,dw-apb-uart";
reg = <0x0 0xff030000 0x0 0x100>;
interrupts = <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&pmucru SCLK_UART0_PMU>, <&pmucru PCLK_UART0_PMU>;
clock-names = "baudclk", "apb_pclk";
reg-shift = <2>;
reg-io-width = <4>;
};
......@@ -6,6 +6,7 @@ Required properties :
- "allwinner,sun8i-a83t-de2-clk"
- "allwinner,sun8i-h3-de2-clk"
- "allwinner,sun8i-v3s-de2-clk"
- "allwinner,sun50i-a64-de2-clk"
- "allwinner,sun50i-h5-de2-clk"
- reg: Must contain the registers base address and length
......
......@@ -27,6 +27,7 @@ config SOC_SAMA5D2
select HAVE_AT91_H32MX
select HAVE_AT91_GENERATED_CLK
select HAVE_AT91_AUDIO_PLL
select HAVE_AT91_I2S_MUX_CLK
select PINCTRL_AT91PIO4
help
Select this if ou are using one of Microchip's SAMA5D2 family SoC.
......@@ -129,6 +130,9 @@ config HAVE_AT91_GENERATED_CLK
config HAVE_AT91_AUDIO_PLL
bool
config HAVE_AT91_I2S_MUX_CLK
bool
config SOC_SAM_V4_V5
bool
......
......@@ -45,6 +45,12 @@ config COMMON_CLK_MAX77686
This driver supports Maxim 77620/77686/77802 crystal oscillator
clock.
config COMMON_CLK_MAX9485
tristate "Maxim 9485 Programmable Clock Generator"
depends on I2C
help
This driver supports Maxim 9485 Programmable Audio Clock Generator
config COMMON_CLK_RK808
tristate "Clock driver for RK805/RK808/RK818"
depends on MFD_RK808
......
......@@ -31,6 +31,7 @@ obj-$(CONFIG_COMMON_CLK_ASPEED) += clk-aspeed.o
obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o
obj-$(CONFIG_CLK_HSDK) += clk-hsdk-pll.o
obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
obj-$(CONFIG_COMMON_CLK_MAX9485) += clk-max9485.o
obj-$(CONFIG_ARCH_MOXART) += clk-moxart.o
obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o
obj-$(CONFIG_ARCH_NPCM7XX) += clk-npcm7xx.o
......
config CLK_ACTIONS
bool "Clock driver for Actions Semi SoCs"
depends on ARCH_ACTIONS || COMPILE_TEST
select REGMAP_MMIO
default ARCH_ACTIONS
if CLK_ACTIONS
# SoC Drivers
config CLK_OWL_S700
bool "Support for the Actions Semi OWL S700 clocks"
depends on (ARM64 && ARCH_ACTIONS) || COMPILE_TEST
default ARM64 && ARCH_ACTIONS
config CLK_OWL_S900
bool "Support for the Actions Semi OWL S900 clocks"
depends on (ARM64 && ARCH_ACTIONS) || COMPILE_TEST
default ARM64 && ARCH_ACTIONS
endif
......@@ -9,4 +9,5 @@ clk-owl-y += owl-composite.o
clk-owl-y += owl-pll.o
# SoC support
obj-$(CONFIG_CLK_OWL_S700) += owl-s700.o
obj-$(CONFIG_CLK_OWL_S900) += owl-s900.o
This diff is collapsed.
......@@ -13,3 +13,4 @@ obj-$(CONFIG_HAVE_AT91_USB_CLK) += clk-usb.o
obj-$(CONFIG_HAVE_AT91_SMD) += clk-smd.o
obj-$(CONFIG_HAVE_AT91_H32MX) += clk-h32mx.o
obj-$(CONFIG_HAVE_AT91_GENERATED_CLK) += clk-generated.o
obj-$(CONFIG_HAVE_AT91_I2S_MUX_CLK) += clk-i2s-mux.o
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2018 Microchip Technology Inc,
* Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
*
*
*/
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <soc/at91/atmel-sfr.h>
#define I2S_BUS_NR 2
struct clk_i2s_mux {
struct clk_hw hw;
struct regmap *regmap;
u8 bus_id;
};
#define to_clk_i2s_mux(hw) container_of(hw, struct clk_i2s_mux, hw)
static u8 clk_i2s_mux_get_parent(struct clk_hw *hw)
{
struct clk_i2s_mux *mux = to_clk_i2s_mux(hw);
u32 val;
regmap_read(mux->regmap, AT91_SFR_I2SCLKSEL, &val);
return (val & BIT(mux->bus_id)) >> mux->bus_id;
}
static int clk_i2s_mux_set_parent(struct clk_hw *hw, u8 index)
{
struct clk_i2s_mux *mux = to_clk_i2s_mux(hw);
return regmap_update_bits(mux->regmap, AT91_SFR_I2SCLKSEL,
BIT(mux->bus_id), index << mux->bus_id);
}
static const struct clk_ops clk_i2s_mux_ops = {
.get_parent = clk_i2s_mux_get_parent,
.set_parent = clk_i2s_mux_set_parent,
.determine_rate = __clk_mux_determine_rate,
};
static struct clk_hw * __init
at91_clk_i2s_mux_register(struct regmap *regmap, const char *name,
const char * const *parent_names,
unsigned int num_parents, u8 bus_id)
{
struct clk_init_data init = {};
struct clk_i2s_mux *i2s_ck;
int ret;
i2s_ck = kzalloc(sizeof(*i2s_ck), GFP_KERNEL);
if (!i2s_ck)
return ERR_PTR(-ENOMEM);
init.name = name;
init.ops = &clk_i2s_mux_ops;
init.parent_names = parent_names;
init.num_parents = num_parents;
i2s_ck->hw.init = &init;
i2s_ck->bus_id = bus_id;
i2s_ck->regmap = regmap;
ret = clk_hw_register(NULL, &i2s_ck->hw);
if (ret) {
kfree(i2s_ck);
return ERR_PTR(ret);
}
return &i2s_ck->hw;
}
static void __init of_sama5d2_clk_i2s_mux_setup(struct device_node *np)
{
struct regmap *regmap_sfr;
u8 bus_id;
const char *parent_names[2];
struct device_node *i2s_mux_np;
struct clk_hw *hw;
int ret;
regmap_sfr = syscon_regmap_lookup_by_compatible("atmel,sama5d2-sfr");
if (IS_ERR(regmap_sfr))
return;
for_each_child_of_node(np, i2s_mux_np) {
if (of_property_read_u8(i2s_mux_np, "reg", &bus_id))
continue;
if (bus_id > I2S_BUS_NR)
continue;
ret = of_clk_parent_fill(i2s_mux_np, parent_names, 2);
if (ret != 2)
continue;
hw = at91_clk_i2s_mux_register(regmap_sfr, i2s_mux_np->name,
parent_names, 2, bus_id);
if (IS_ERR(hw))
continue;
of_clk_add_hw_provider(i2s_mux_np, of_clk_hw_simple_get, hw);
}
}
CLK_OF_DECLARE(sama5d2_clk_i2s_mux, "atmel,sama5d2-clk-i2s-mux",
of_sama5d2_clk_i2s_mux_setup);
......@@ -109,7 +109,7 @@ static const struct aspeed_gate_data aspeed_gates[] = {
[ASPEED_CLK_GATE_RSACLK] = { 24, -1, "rsaclk-gate", NULL, 0 }, /* RSA */
[ASPEED_CLK_GATE_UART3CLK] = { 25, -1, "uart3clk-gate", "uart", 0 }, /* UART3 */
[ASPEED_CLK_GATE_UART4CLK] = { 26, -1, "uart4clk-gate", "uart", 0 }, /* UART4 */
[ASPEED_CLK_GATE_SDCLKCLK] = { 27, 16, "sdclk-gate", NULL, 0 }, /* SDIO/SD */
[ASPEED_CLK_GATE_SDCLK] = { 27, 16, "sdclk-gate", NULL, 0 }, /* SDIO/SD */
[ASPEED_CLK_GATE_LHCCLK] = { 28, -1, "lhclk-gate", "lhclk", 0 }, /* LPC master/LPC+ */
};
......
// SPDX-License-Identifier: GPL-2.0
/*
* CS2000 -- CIRRUS LOGIC Fractional-N Clock Synthesizer & Clock Multiplier
*
* Copyright (C) 2015 Renesas Electronics Corporation
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/clk-provider.h>
#include <linux/delay.h>
......
......@@ -177,8 +177,15 @@ static struct clk *_of_fixed_factor_clk_setup(struct device_node *node)
clk = clk_register_fixed_factor(NULL, clk_name, parent_name, flags,
mult, div);
if (IS_ERR(clk))
if (IS_ERR(clk)) {
/*
* If parent clock is not registered, registration would fail.
* Clear OF_POPULATED flag so that clock registration can be
* attempted again from probe function.
*/
of_node_clear_flag(node, OF_POPULATED);
return clk;
}
ret = of_clk_add_provider(node, of_clk_src_simple_get, clk);
if (ret) {
......
// SPDX-License-Identifier: GPL-2.0
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/regulator/consumer.h>
#include <dt-bindings/clock/maxim,max9485.h>
#define MAX9485_NUM_CLKS 4
/* This chip has only one register of 8 bit width. */
#define MAX9485_FS_12KHZ (0 << 0)
#define MAX9485_FS_32KHZ (1 << 0)
#define MAX9485_FS_44_1KHZ (2 << 0)
#define MAX9485_FS_48KHZ (3 << 0)
#define MAX9485_SCALE_256 (0 << 2)
#define MAX9485_SCALE_384 (1 << 2)
#define MAX9485_SCALE_768 (2 << 2)
#define MAX9485_DOUBLE BIT(4)
#define MAX9485_CLKOUT1_ENABLE BIT(5)
#define MAX9485_CLKOUT2_ENABLE BIT(6)
#define MAX9485_MCLK_ENABLE BIT(7)
#define MAX9485_FREQ_MASK 0x1f
struct max9485_rate {
unsigned long out;
u8 reg_value;
};
/*
* Ordered by frequency. For frequency the hardware can generate with
* multiple settings, the one with lowest jitter is listed first.
*/
static const struct max9485_rate max9485_rates[] = {
{ 3072000, MAX9485_FS_12KHZ | MAX9485_SCALE_256 },
{ 4608000, MAX9485_FS_12KHZ | MAX9485_SCALE_384 },
{ 8192000, MAX9485_FS_32KHZ | MAX9485_SCALE_256 },
{ 9126000, MAX9485_FS_12KHZ | MAX9485_SCALE_768 },
{ 11289600, MAX9485_FS_44_1KHZ | MAX9485_SCALE_256 },
{ 12288000, MAX9485_FS_48KHZ | MAX9485_SCALE_256 },
{ 12288000, MAX9485_FS_32KHZ | MAX9485_SCALE_384 },
{ 16384000, MAX9485_FS_32KHZ | MAX9485_SCALE_256 | MAX9485_DOUBLE },
{ 16934400, MAX9485_FS_44_1KHZ | MAX9485_SCALE_384 },
{ 18384000, MAX9485_FS_48KHZ | MAX9485_SCALE_384 },
{ 22579200, MAX9485_FS_44_1KHZ | MAX9485_SCALE_256 | MAX9485_DOUBLE },
{ 24576000, MAX9485_FS_48KHZ | MAX9485_SCALE_256 | MAX9485_DOUBLE },
{ 24576000, MAX9485_FS_32KHZ | MAX9485_SCALE_384 | MAX9485_DOUBLE },
{ 24576000, MAX9485_FS_32KHZ | MAX9485_SCALE_768 },
{ 33868800, MAX9485_FS_44_1KHZ | MAX9485_SCALE_384 | MAX9485_DOUBLE },
{ 33868800, MAX9485_FS_44_1KHZ | MAX9485_SCALE_768 },
{ 36864000, MAX9485_FS_48KHZ | MAX9485_SCALE_384 | MAX9485_DOUBLE },
{ 36864000, MAX9485_FS_48KHZ | MAX9485_SCALE_768 },
{ 49152000, MAX9485_FS_32KHZ | MAX9485_SCALE_768 | MAX9485_DOUBLE },
{ 67737600, MAX9485_FS_44_1KHZ | MAX9485_SCALE_768 | MAX9485_DOUBLE },
{ 73728000, MAX9485_FS_48KHZ | MAX9485_SCALE_768 | MAX9485_DOUBLE },
{ } /* sentinel */
};
struct max9485_driver_data;
struct max9485_clk_hw {
struct clk_hw hw;
struct clk_init_data init;
u8 enable_bit;
struct max9485_driver_data *drvdata;
};
struct max9485_driver_data {
struct clk *xclk;
struct i2c_client *client;
u8 reg_value;
struct regulator *supply;
struct gpio_desc *reset_gpio;
struct max9485_clk_hw hw[MAX9485_NUM_CLKS];
};
static inline struct max9485_clk_hw *to_max9485_clk(struct clk_hw *hw)
{
return container_of(hw, struct max9485_clk_hw, hw);
}
static int max9485_update_bits(struct max9485_driver_data *drvdata,
u8 mask, u8 value)
{
int ret;
drvdata->reg_value &= ~mask;
drvdata->reg_value |= value;
dev_dbg(&drvdata->client->dev,
"updating mask 0x%02x value 0x%02x -> 0x%02x\n",
mask, value, drvdata->reg_value);
ret = i2c_master_send(drvdata->client,
&drvdata->reg_value,
sizeof(drvdata->reg_value));
return ret < 0 ? ret : 0;
}
static int max9485_clk_prepare(struct clk_hw *hw)
{
struct max9485_clk_hw *clk_hw = to_max9485_clk(hw);
return max9485_update_bits(clk_hw->drvdata,
clk_hw->enable_bit,
clk_hw->enable_bit);
}
static void max9485_clk_unprepare(struct clk_hw *hw)
{
struct max9485_clk_hw *clk_hw = to_max9485_clk(hw);
max9485_update_bits(clk_hw->drvdata, clk_hw->enable_bit, 0);
}
/*
* CLKOUT - configurable clock output
*/
static int max9485_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct max9485_clk_hw *clk_hw = to_max9485_clk(hw);
const struct max9485_rate *entry;
for (entry = max9485_rates; entry->out != 0; entry++)
if (entry->out == rate)
break;
if (entry->out == 0)
return -EINVAL;
return max9485_update_bits(clk_hw->drvdata,
MAX9485_FREQ_MASK,
entry->reg_value);
}
static unsigned long max9485_clkout_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct max9485_clk_hw *clk_hw = to_max9485_clk(hw);
struct max9485_driver_data *drvdata = clk_hw->drvdata;
u8 val = drvdata->reg_value & MAX9485_FREQ_MASK;
const struct max9485_rate *entry;
for (entry = max9485_rates; entry->out != 0; entry++)
if (val == entry->reg_value)
return entry->out;
return 0;
}
static long max9485_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
const struct max9485_rate *curr, *prev = NULL;
for (curr = max9485_rates; curr->out != 0; curr++) {
/* Exact matches */
if (curr->out == rate)
return rate;
/*
* Find the first entry that has a frequency higher than the
* requested one.
*/
if (curr->out > rate) {
unsigned int mid;
/*
* If this is the first entry, clamp the value to the
* lowest possible frequency.
*/
if (!prev)
return curr->out;
/*
* Otherwise, determine whether the previous entry or
* current one is closer.
*/
mid = prev->out + ((curr->out - prev->out) / 2);
return (mid > rate) ? prev->out : curr->out;
}
prev = curr;
}
/* If the last entry was still too high, clamp the value */
return prev->out;
}
struct max9485_clk {
const char *name;
int parent_index;
const struct clk_ops ops;
u8 enable_bit;
};
static const struct max9485_clk max9485_clks[MAX9485_NUM_CLKS] = {
[MAX9485_MCLKOUT] = {
.name = "mclkout",
.parent_index = -1,
.enable_bit = MAX9485_MCLK_ENABLE,
.ops = {
.prepare = max9485_clk_prepare,
.unprepare = max9485_clk_unprepare,
},
},
[MAX9485_CLKOUT] = {
.name = "clkout",
.parent_index = -1,
.ops = {
.set_rate = max9485_clkout_set_rate,
.round_rate = max9485_clkout_round_rate,
.recalc_rate = max9485_clkout_recalc_rate,
},
},
[MAX9485_CLKOUT1] = {
.name = "clkout1",
.parent_index = MAX9485_CLKOUT,
.enable_bit = MAX9485_CLKOUT1_ENABLE,
.ops = {
.prepare = max9485_clk_prepare,
.unprepare = max9485_clk_unprepare,
},
},
[MAX9485_CLKOUT2] = {
.name = "clkout2",
.parent_index = MAX9485_CLKOUT,
.enable_bit = MAX9485_CLKOUT2_ENABLE,
.ops = {
.prepare = max9485_clk_prepare,
.unprepare = max9485_clk_unprepare,
},
},
};
static struct clk_hw *
max9485_of_clk_get(struct of_phandle_args *clkspec, void *data)
{
struct max9485_driver_data *drvdata = data;
unsigned int idx = clkspec->args[0];
return &drvdata->hw[idx].hw;
}
static int max9485_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct max9485_driver_data *drvdata;
struct device *dev = &client->dev;
const char *xclk_name;
int i, ret;
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
drvdata->xclk = devm_clk_get(dev, "xclk");
if (IS_ERR(drvdata->xclk))
return PTR_ERR(drvdata->xclk);
xclk_name = __clk_get_name(drvdata->xclk);
drvdata->supply = devm_regulator_get(dev, "vdd");
if (IS_ERR(drvdata->supply))
return PTR_ERR(drvdata->supply);
ret = regulator_enable(drvdata->supply);
if (ret < 0)
return ret;
drvdata->reset_gpio =
devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(drvdata->reset_gpio))
return PTR_ERR(drvdata->reset_gpio);
i2c_set_clientdata(client, drvdata);
drvdata->client = client;
ret = i2c_master_recv(drvdata->client, &drvdata->reg_value,
sizeof(drvdata->reg_value));
if (ret < 0) {
dev_warn(dev, "Unable to read device register: %d\n", ret);
return ret;
}
for (i = 0; i < MAX9485_NUM_CLKS; i++) {
int parent_index = max9485_clks[i].parent_index;
const char *name;
if (of_property_read_string_index(dev->of_node,
"clock-output-names",
i, &name) == 0) {
drvdata->hw[i].init.name = name;
} else {
drvdata->hw[i].init.name = max9485_clks[i].name;
}
drvdata->hw[i].init.ops = &max9485_clks[i].ops;
drvdata->hw[i].init.num_parents = 1;
drvdata->hw[i].init.flags = 0;
if (parent_index > 0) {
drvdata->hw[i].init.parent_names =
&drvdata->hw[parent_index].init.name;
drvdata->hw[i].init.flags |= CLK_SET_RATE_PARENT;
} else {
drvdata->hw[i].init.parent_names = &xclk_name;
}
drvdata->hw[i].enable_bit = max9485_clks[i].enable_bit;
drvdata->hw[i].hw.init = &drvdata->hw[i].init;
drvdata->hw[i].drvdata = drvdata;
ret = devm_clk_hw_register(dev, &drvdata->hw[i].hw);
if (ret < 0)
return ret;
}
return devm_of_clk_add_hw_provider(dev, max9485_of_clk_get, drvdata);
}
static int __maybe_unused max9485_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct max9485_driver_data *drvdata = i2c_get_clientdata(client);
gpiod_set_value_cansleep(drvdata->reset_gpio, 0);
return 0;
}
static int __maybe_unused max9485_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct max9485_driver_data *drvdata = i2c_get_clientdata(client);
int ret;
gpiod_set_value_cansleep(drvdata->reset_gpio, 1);
ret = i2c_master_send(client, &drvdata->reg_value,
sizeof(drvdata->reg_value));
return ret < 0 ? ret : 0;
}
static const struct dev_pm_ops max9485_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(max9485_suspend, max9485_resume)
};
static const struct of_device_id max9485_dt_ids[] = {
{ .compatible = "maxim,max9485", },
{ }
};
MODULE_DEVICE_TABLE(of, max9485_dt_ids);
static const struct i2c_device_id max9485_i2c_ids[] = {
{ .name = "max9485", },
{ }
};
MODULE_DEVICE_TABLE(i2c, max9485_i2c_ids);
static struct i2c_driver max9485_driver = {
.driver = {
.name = "max9485",
.pm = &max9485_pm_ops,
.of_match_table = max9485_dt_ids,
},
.probe = max9485_i2c_probe,
.id_table = max9485_i2c_ids,
};
module_i2c_driver(max9485_driver);
MODULE_AUTHOR("Daniel Mack <daniel@zonque.org>");
MODULE_DESCRIPTION("MAX9485 Programmable Audio Clock Generator");
MODULE_LICENSE("GPL v2");
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment