Merge branches 'clk-davinci', 'clk-si544', 'clk-rockchip', 'clk-uniphier' and 'clk-ti-flag-fix' into clk-next

* clk-davinci:
  clk: davinci: Remove redundant dev_err calls
  clk: davinci: cfgchip: Add TI DA8XX USB PHY clocks
  clk: davinci: New driver for TI DA8XX CFGCHIP clocks
  dt-bindings: clock: Add bindings for DA8XX CFGCHIP clocks
  clk: davinci: Add platform information for TI DM646x PSC
  clk: davinci: Add platform information for TI DM644x PSC
  clk: davinci: Add platform information for TI DM365 PSC
  clk: davinci: Add platform information for TI DM355 PSC
  clk: davinci: Add platform information for TI DA850 PSC
  clk: davinci: Add platform information for TI DA830 PSC
  clk: davinci: New driver for davinci PSC clocks
  dt-bindings: clock: New bindings for TI Davinci PSC
  clk: davinci: Add platform information for TI DM646x PLL
  clk: davinci: Add platform information for TI DM644x PLL
  clk: davinci: Add platform information for TI DM365 PLL
  clk: davinci: Add platform information for TI DM355 PLL
  clk: davinci: Add platform information for TI DA850 PLL
  clk: davinci: Add platform information for TI DA830 PLL
  clk: davinci: New driver for davinci PLL clocks
  dt-bindings: clock: Add new bindings for TI Davinci PLL clocks

* clk-si544:
  clk: Add driver for the si544 clock generator chip

* clk-rockchip:
  clk: rockchip: assign correct id for pclk_ddr and hclk_sd in rk3399
  clk: rockchip: Fix error return in phase clock registration
  clk: rockchip: Correct the behaviour of restoring cached phase
  clk: rockchip: Fix wrong parents for MMC phase clock for rk3328
  clk: rockchip: Fix wrong parent for SDMMC phase clock for rk3228
  clk: rockchip: Add 1.6GHz PLL rate for rk3399
  clk: rockchip: Restore the clock phase after the rate was changed
  clk: rockchip: Prevent calculating mmc phase if clock rate is zero
  clk: rockchip: Free the memory on the error path
  clk: rockchip: document hdmi_phy external input for rk3328
  clk: rockchip: add flags for rk3328 dclk_lcdc
  clk: rockchip: remove ignore_unused flag from rk3328 vio_h2p clocks
  clk: rockchip: protect all remaining rk3328 interconnect clocks
  clk: rockchip: export sclk_hdmi_sfc on rk3328
  clk: rockchip: remove HCLK_VIO from rk3328 dt header
  clk: rockchip: fix hclk_vio_niu on rk3328

* clk-uniphier:
  clk: uniphier: add additional ethernet clock lines for Pro4
  clk: uniphier: add SATA clock control support
  clk: uniphier: add PCIe clock control support
  clk: uniphier: add ethernet clock control support for PXs3
  clk: uniphier: add Pro4/Pro5/PXs2 audio system clock

* clk-ti-flag-fix:
  clk: ti: fix flag space conflict with clkctrl clocks
  clk: ti: clkctrl: add support for CLK_SET_RATE_PARENT flag
This commit is contained in:
Stephen Boyd 2018-04-06 13:22:06 -07:00
38 changed files with 4729 additions and 46 deletions

View File

@ -32,6 +32,7 @@ clock-output-names:
- "clkin_i2s" - external I2S clock - optional,
- "gmac_clkin" - external GMAC clock - optional
- "phy_50m_out" - output clock of the pll in the mac phy
- "hdmi_phy" - output clock of the hdmi phy pll - optional
Example: Clock controller node:

View File

@ -0,0 +1,25 @@
Binding for Silicon Labs 544 programmable I2C clock generator.
Reference
This binding uses the common clock binding[1]. Details about the device can be
found in the datasheet[2].
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
[2] Si544 datasheet
https://www.silabs.com/documents/public/data-sheets/si544-datasheet.pdf
Required properties:
- compatible: One of "silabs,si514a", "silabs,si514b" "silabs,si514c" according
to the speed grade of the chip.
- reg: I2C device address.
- #clock-cells: From common clock bindings: Shall be 0.
Optional properties:
- clock-output-names: From common clock bindings. Recommended to be "si544".
Example:
si544: clock-controller@55 {
reg = <0x55>;
#clock-cells = <0>;
compatible = "silabs,si544b";
};

View File

@ -0,0 +1,93 @@
Binding for TI DA8XX/OMAP-L13X/AM17XX/AM18XX CFGCHIP clocks
TI DA8XX/OMAP-L13X/AM17XX/AM18XX SoCs contain a general purpose set of
registers call CFGCHIPn. Some of these registers function as clock
gates. This document describes the bindings for those clocks.
All of the clock nodes described below must be child nodes of a CFGCHIP node
(compatible = "ti,da830-cfgchip").
USB PHY clocks
--------------
Required properties:
- compatible: shall be "ti,da830-usb-phy-clocks".
- #clock-cells: from common clock binding; shall be set to 1.
- clocks: phandles to the parent clocks corresponding to clock-names
- clock-names: shall be "fck", "usb_refclkin", "auxclk"
This node provides two clocks. The clock at index 0 is the USB 2.0 PHY 48MHz
clock and the clock at index 1 is the USB 1.1 PHY 48MHz clock.
eHRPWM Time Base Clock (TBCLK)
------------------------------
Required properties:
- compatible: shall be "ti,da830-tbclksync".
- #clock-cells: from common clock binding; shall be set to 0.
- clocks: phandle to the parent clock
- clock-names: shall be "fck"
PLL DIV4.5 divider
------------------
Required properties:
- compatible: shall be "ti,da830-div4p5ena".
- #clock-cells: from common clock binding; shall be set to 0.
- clocks: phandle to the parent clock
- clock-names: shall be "pll0_pllout"
EMIFA clock source (ASYNC1)
---------------------------
Required properties:
- compatible: shall be "ti,da850-async1-clksrc".
- #clock-cells: from common clock binding; shall be set to 0.
- clocks: phandles to the parent clocks corresponding to clock-names
- clock-names: shall be "pll0_sysclk3", "div4.5"
ASYNC3 clock source
-------------------
Required properties:
- compatible: shall be "ti,da850-async3-clksrc".
- #clock-cells: from common clock binding; shall be set to 0.
- clocks: phandles to the parent clocks corresponding to clock-names
- clock-names: shall be "pll0_sysclk2", "pll1_sysclk2"
Examples:
cfgchip: syscon@1417c {
compatible = "ti,da830-cfgchip", "syscon", "simple-mfd";
reg = <0x1417c 0x14>;
usb_phy_clk: usb-phy-clocks {
compatible = "ti,da830-usb-phy-clocks";
#clock-cells = <1>;
clocks = <&psc1 1>, <&usb_refclkin>, <&pll0_auxclk>;
clock-names = "fck", "usb_refclkin", "auxclk";
};
ehrpwm_tbclk: ehrpwm_tbclk {
compatible = "ti,da830-tbclksync";
#clock-cells = <0>;
clocks = <&psc1 17>;
clock-names = "fck";
};
div4p5_clk: div4.5 {
compatible = "ti,da830-div4p5ena";
#clock-cells = <0>;
clocks = <&pll0_pllout>;
clock-names = "pll0_pllout";
};
async1_clk: async1 {
compatible = "ti,da850-async1-clksrc";
#clock-cells = <0>;
clocks = <&pll0_sysclk 3>, <&div4p5_clk>;
clock-names = "pll0_sysclk3", "div4.5";
};
async3_clk: async3 {
compatible = "ti,da850-async3-clksrc";
#clock-cells = <0>;
clocks = <&pll0_sysclk 2>, <&pll1_sysclk 2>;
clock-names = "pll0_sysclk2", "pll1_sysclk2";
};
};
Also see:
- Documentation/devicetree/bindings/clock/clock-bindings.txt

View File

@ -0,0 +1,96 @@
Binding for TI DaVinci PLL Controllers
The PLL provides clocks to most of the components on the SoC. In addition
to the PLL itself, this controller also contains bypasses, gates, dividers,
an multiplexers for various clock signals.
Required properties:
- compatible: shall be one of:
- "ti,da850-pll0" for PLL0 on DA850/OMAP-L138/AM18XX
- "ti,da850-pll1" for PLL1 on DA850/OMAP-L138/AM18XX
- reg: physical base address and size of the controller's register area.
- clocks: phandles corresponding to the clock names
- clock-names: names of the clock sources - depends on compatible string
- for "ti,da850-pll0", shall be "clksrc", "extclksrc"
- for "ti,da850-pll1", shall be "clksrc"
Optional properties:
- ti,clkmode-square-wave: Indicates that the the board is supplying a square
wave input on the OSCIN pin instead of using a crystal oscillator.
This property is only valid when compatible = "ti,da850-pll0".
Optional child nodes:
pllout
Describes the main PLL clock output (before POSTDIV). The node name must
be "pllout".
Required properties:
- #clock-cells: shall be 0
sysclk
Describes the PLLDIVn divider clocks that provide the SYSCLKn clock
domains. The node name must be "sysclk". Consumers of this node should
use "n" in "SYSCLKn" as the index parameter for the clock cell.
Required properties:
- #clock-cells: shall be 1
auxclk
Describes the AUXCLK output of the PLL. The node name must be "auxclk".
This child node is only valid when compatible = "ti,da850-pll0".
Required properties:
- #clock-cells: shall be 0
obsclk
Describes the OBSCLK output of the PLL. The node name must be "obsclk".
Required properties:
- #clock-cells: shall be 0
Examples:
pll0: clock-controller@11000 {
compatible = "ti,da850-pll0";
reg = <0x11000 0x1000>;
clocks = <&ref_clk>, <&pll1_sysclk 3>;
clock-names = "clksrc", "extclksrc";
ti,clkmode-square-wave;
pll0_pllout: pllout {
#clock-cells = <0>;
};
pll0_sysclk: sysclk {
#clock-cells = <1>;
};
pll0_auxclk: auxclk {
#clock-cells = <0>;
};
pll0_obsclk: obsclk {
#clock-cells = <0>;
};
};
pll1: clock-controller@21a000 {
compatible = "ti,da850-pll1";
reg = <0x21a000 0x1000>;
clocks = <&ref_clk>;
clock-names = "clksrc";
pll0_sysclk: sysclk {
#clock-cells = <1>;
};
pll0_obsclk: obsclk {
#clock-cells = <0>;
};
};
Also see:
- Documentation/devicetree/bindings/clock/clock-bindings.txt

View File

@ -0,0 +1,71 @@
Binding for TI DaVinci Power Sleep Controller (PSC)
The PSC provides power management, clock gating and reset functionality. It is
primarily used for clocking.
Required properties:
- compatible: shall be one of:
- "ti,da850-psc0" for PSC0 on DA850/OMAP-L138/AM18XX
- "ti,da850-psc1" for PSC1 on DA850/OMAP-L138/AM18XX
- reg: physical base address and size of the controller's register area
- #clock-cells: from common clock binding; shall be set to 1
- #power-domain-cells: from generic power domain binding; shall be set to 1.
- clocks: phandles to clocks corresponding to the clock-names property
- clock-names: list of parent clock names - depends on compatible value
- for "ti,da850-psc0", shall be "pll0_sysclk1", "pll0_sysclk2",
"pll0_sysclk4", "pll0_sysclk6", "async1"
- for "ti,da850-psc1", shall be "pll0_sysclk2", "pll0_sysclk4", "async3"
Optional properties:
- #reset-cells: from reset binding; shall be set to 1 - only applicable when
at least one local domain provides a local reset.
Consumers:
Clock, power domain and reset consumers shall use the local power domain
module ID (LPSC) as the index corresponding to the clock cell. Refer to
the device-specific datasheet to find these numbers. NB: Most local
domains only provide a clock/power domain and not a reset.
Examples:
psc0: clock-controller@10000 {
compatible = "ti,da850-psc0";
reg = <0x10000 0x1000>;
#clock-cells = <1>;
#power-domain-cells = <1>;
#reset-cells = <1>;
clocks = <&pll0_sysclk 1>, <&pll0_sysclk 2>,
<&pll0_sysclk 4>, <&pll0_sysclk 6>, <&async1_clk>;
clock_names = "pll0_sysclk1", "pll0_sysclk2",
"pll0_sysclk4", "pll0_sysclk6", "async1";
};
psc1: clock-controller@227000 {
compatible = "ti,da850-psc1";
reg = <0x227000 0x1000>;
#clock-cells = <1>;
#power-domain-cells = <1>;
clocks = <&pll0_sysclk 2>, <&pll0_sysclk 4>, <&async3_clk>;
clock_names = "pll0_sysclk2", "pll0_sysclk4", "async3";
};
/* consumer */
dsp: dsp@11800000 {
compatible = "ti,da850-dsp";
reg = <0x11800000 0x40000>,
<0x11e00000 0x8000>,
<0x11f00000 0x8000>,
<0x01c14044 0x4>,
<0x01c14174 0x8>;
reg-names = "l2sram", "l1pram", "l1dram", "host1cfg", "chipsig";
interrupt-parent = <&intc>;
interrupts = <28>;
clocks = <&psc0 15>;
power-domains = <&psc0 15>;
resets = <&psc0 15>;
};
Also see:
- Documentation/devicetree/bindings/clock/clock-bindings.txt
- Documentation/devicetree/bindings/power/power_domain.txt
- Documentation/devicetree/bindings/reset/reset.txt

View File

@ -13793,6 +13793,13 @@ F: arch/arm/mach-davinci/
F: drivers/i2c/busses/i2c-davinci.c
F: arch/arm/boot/dts/da850*
TI DAVINCI SERIES CLOCK DRIVER
M: David Lechner <david@lechnology.com>
R: Sekhar Nori <nsekhar@ti.com>
S: Maintained
F: Documentation/devicetree/bindings/clock/ti/davinci/
F: drivers/clk/davinci/
TI DAVINCI SERIES GPIO DRIVER
M: Keerthy <j-keerthy@ti.com>
L: linux-gpio@vger.kernel.org

View File

@ -93,6 +93,15 @@ config COMMON_CLK_SI514
This driver supports the Silicon Labs 514 programmable clock
generator.
config COMMON_CLK_SI544
tristate "Clock driver for SiLabs 544 devices"
depends on I2C
select REGMAP_I2C
help
---help---
This driver supports the Silicon Labs 544 programmable clock
generator.
config COMMON_CLK_SI570
tristate "Clock driver for SiLabs 570 and compatible devices"
depends on I2C

View File

@ -44,6 +44,7 @@ obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o
obj-$(CONFIG_COMMON_CLK_SCPI) += clk-scpi.o
obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o
obj-$(CONFIG_COMMON_CLK_SI514) += clk-si514.o
obj-$(CONFIG_COMMON_CLK_SI544) += clk-si544.o
obj-$(CONFIG_COMMON_CLK_SI570) += clk-si570.o
obj-$(CONFIG_COMMON_CLK_STM32F) += clk-stm32f4.o
obj-$(CONFIG_COMMON_CLK_STM32H7) += clk-stm32h7.o
@ -62,6 +63,7 @@ obj-$(CONFIG_ARCH_ARTPEC) += axis/
obj-$(CONFIG_ARC_PLAT_AXS10X) += axs10x/
obj-y += bcm/
obj-$(CONFIG_ARCH_BERLIN) += berlin/
obj-$(CONFIG_ARCH_DAVINCI) += davinci/
obj-$(CONFIG_H8300) += h8300/
obj-$(CONFIG_ARCH_HISI) += hisilicon/
obj-y += imgtec/

411
drivers/clk/clk-si544.c Normal file
View File

@ -0,0 +1,411 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Driver for Silicon Labs Si544 Programmable Oscillator
* Copyright (C) 2018 Topic Embedded Products
* Author: Mike Looijmans <mike.looijmans@topic.nl>
*/
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/slab.h>
/* I2C registers (decimal as in datasheet) */
#define SI544_REG_CONTROL 7
#define SI544_REG_OE_STATE 17
#define SI544_REG_HS_DIV 23
#define SI544_REG_LS_HS_DIV 24
#define SI544_REG_FBDIV0 26
#define SI544_REG_FBDIV8 27
#define SI544_REG_FBDIV16 28
#define SI544_REG_FBDIV24 29
#define SI544_REG_FBDIV32 30
#define SI544_REG_FBDIV40 31
#define SI544_REG_FCAL_OVR 69
#define SI544_REG_ADPLL_DELTA_M0 231
#define SI544_REG_ADPLL_DELTA_M8 232
#define SI544_REG_ADPLL_DELTA_M16 233
#define SI544_REG_PAGE_SELECT 255
/* Register values */
#define SI544_CONTROL_RESET BIT(7)
#define SI544_CONTROL_MS_ICAL2 BIT(3)
#define SI544_OE_STATE_ODC_OE BIT(0)
/* Max freq depends on speed grade */
#define SI544_MIN_FREQ 200000U
/* Si544 Internal oscilator runs at 55.05 MHz */
#define FXO 55050000U
/* VCO range is 10.8 .. 12.1 GHz, max depends on speed grade */
#define FVCO_MIN 10800000000ULL
#define HS_DIV_MAX 2046
#define HS_DIV_MAX_ODD 33
/* Lowest frequency synthesizeable using only the HS divider */
#define MIN_HSDIV_FREQ (FVCO_MIN / HS_DIV_MAX)
enum si544_speed_grade {
si544a,
si544b,
si544c,
};
struct clk_si544 {
struct clk_hw hw;
struct regmap *regmap;
struct i2c_client *i2c_client;
enum si544_speed_grade speed_grade;
};
#define to_clk_si544(_hw) container_of(_hw, struct clk_si544, hw)
/**
* struct clk_si544_muldiv - Multiplier/divider settings
* @fb_div_frac: integer part of feedback divider (32 bits)
* @fb_div_int: fractional part of feedback divider (11 bits)
* @hs_div: 1st divider, 5..2046, must be even when >33
* @ls_div_bits: 2nd divider, as 2^x, range 0..5
* If ls_div_bits is non-zero, hs_div must be even
*/
struct clk_si544_muldiv {
u32 fb_div_frac;
u16 fb_div_int;
u16 hs_div;
u8 ls_div_bits;
};
/* Enables or disables the output driver */
static int si544_enable_output(struct clk_si544 *data, bool enable)
{
return regmap_update_bits(data->regmap, SI544_REG_OE_STATE,
SI544_OE_STATE_ODC_OE, enable ? SI544_OE_STATE_ODC_OE : 0);
}
/* Retrieve clock multiplier and dividers from hardware */
static int si544_get_muldiv(struct clk_si544 *data,
struct clk_si544_muldiv *settings)
{
int err;
u8 reg[6];
err = regmap_bulk_read(data->regmap, SI544_REG_HS_DIV, reg, 2);
if (err)
return err;
settings->ls_div_bits = (reg[1] >> 4) & 0x07;
settings->hs_div = (reg[1] & 0x07) << 8 | reg[0];
err = regmap_bulk_read(data->regmap, SI544_REG_FBDIV0, reg, 6);
if (err)
return err;
settings->fb_div_int = reg[4] | (reg[5] & 0x07) << 8;
settings->fb_div_frac = reg[0] | reg[1] << 8 | reg[2] << 16 |
reg[3] << 24;
return 0;
}
static int si544_set_muldiv(struct clk_si544 *data,
struct clk_si544_muldiv *settings)
{
int err;
u8 reg[6];
reg[0] = settings->hs_div;
reg[1] = settings->hs_div >> 8 | settings->ls_div_bits << 4;
err = regmap_bulk_write(data->regmap, SI544_REG_HS_DIV, reg, 2);
if (err < 0)
return err;
reg[0] = settings->fb_div_frac;
reg[1] = settings->fb_div_frac >> 8;
reg[2] = settings->fb_div_frac >> 16;
reg[3] = settings->fb_div_frac >> 24;
reg[4] = settings->fb_div_int;
reg[5] = settings->fb_div_int >> 8;
/*
* Writing to SI544_REG_FBDIV40 triggers the clock change, so that
* must be written last
*/
return regmap_bulk_write(data->regmap, SI544_REG_FBDIV0, reg, 6);
}
static bool is_valid_frequency(const struct clk_si544 *data,
unsigned long frequency)
{
unsigned long max_freq = 0;
if (frequency < SI544_MIN_FREQ)
return false;
switch (data->speed_grade) {
case si544a:
max_freq = 1500000000;
break;
case si544b:
max_freq = 800000000;
break;
case si544c:
max_freq = 350000000;
break;
}
return frequency <= max_freq;
}
/* Calculate divider settings for a given frequency */
static int si544_calc_muldiv(struct clk_si544_muldiv *settings,
unsigned long frequency)
{
u64 vco;
u32 ls_freq;
u32 tmp;
u8 res;
/* Determine the minimum value of LS_DIV and resulting target freq. */
ls_freq = frequency;
settings->ls_div_bits = 0;
if (frequency >= MIN_HSDIV_FREQ) {
settings->ls_div_bits = 0;
} else {
res = 1;
tmp = 2 * HS_DIV_MAX;
while (tmp <= (HS_DIV_MAX * 32)) {
if (((u64)frequency * tmp) >= FVCO_MIN)
break;
++res;
tmp <<= 1;
}
settings->ls_div_bits = res;
ls_freq = frequency << res;
}
/* Determine minimum HS_DIV by rounding up */
vco = FVCO_MIN + ls_freq - 1;
do_div(vco, ls_freq);
settings->hs_div = vco;
/* round up to even number when required */
if ((settings->hs_div & 1) &&
(settings->hs_div > HS_DIV_MAX_ODD || settings->ls_div_bits))
++settings->hs_div;
/* Calculate VCO frequency (in 10..12GHz range) */
vco = (u64)ls_freq * settings->hs_div;
/* Calculate the integer part of the feedback divider */
tmp = do_div(vco, FXO);
settings->fb_div_int = vco;
/* And the fractional bits using the remainder */
vco = (u64)tmp << 32;
do_div(vco, FXO);
settings->fb_div_frac = vco;
return 0;
}
/* Calculate resulting frequency given the register settings */
static unsigned long si544_calc_rate(struct clk_si544_muldiv *settings)
{
u32 d = settings->hs_div * BIT(settings->ls_div_bits);
u64 vco;
/* Calculate VCO from the fractional part */
vco = (u64)settings->fb_div_frac * FXO;
vco += (FXO / 2);
vco >>= 32;
/* Add the integer part of the VCO frequency */
vco += (u64)settings->fb_div_int * FXO;
/* Apply divider to obtain the generated frequency */
do_div(vco, d);
return vco;
}
static unsigned long si544_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_si544 *data = to_clk_si544(hw);
struct clk_si544_muldiv settings;
int err;
err = si544_get_muldiv(data, &settings);
if (err)
return 0;
return si544_calc_rate(&settings);
}
static long si544_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct clk_si544 *data = to_clk_si544(hw);
struct clk_si544_muldiv settings;
int err;
if (!is_valid_frequency(data, rate))
return -EINVAL;
err = si544_calc_muldiv(&settings, rate);
if (err)
return err;
return si544_calc_rate(&settings);
}
/*
* Update output frequency for "big" frequency changes
*/
static int si544_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_si544 *data = to_clk_si544(hw);
struct clk_si544_muldiv settings;
int err;
if (!is_valid_frequency(data, rate))
return -EINVAL;
err = si544_calc_muldiv(&settings, rate);
if (err)
return err;
si544_enable_output(data, false);
/* Allow FCAL for this frequency update */
err = regmap_write(data->regmap, SI544_REG_FCAL_OVR, 0);
if (err < 0)
return err;
err = si544_set_muldiv(data, &settings);
if (err < 0)
return err; /* Undefined state now, best to leave disabled */
/* Trigger calibration */
err = regmap_write(data->regmap, SI544_REG_CONTROL,
SI544_CONTROL_MS_ICAL2);
if (err < 0)
return err;
/* Applying a new frequency can take up to 10ms */
usleep_range(10000, 12000);
si544_enable_output(data, true);
return err;
}
static const struct clk_ops si544_clk_ops = {
.recalc_rate = si544_recalc_rate,
.round_rate = si544_round_rate,
.set_rate = si544_set_rate,
};
static bool si544_regmap_is_volatile(struct device *dev, unsigned int reg)
{
switch (reg) {
case SI544_REG_CONTROL:
case SI544_REG_FCAL_OVR:
return true;
default:
return false;
}
}
static const struct regmap_config si544_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.cache_type = REGCACHE_RBTREE,
.max_register = SI544_REG_PAGE_SELECT,
.volatile_reg = si544_regmap_is_volatile,
};
static int si544_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct clk_si544 *data;
struct clk_init_data init;
int err;
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
init.ops = &si544_clk_ops;
init.flags = 0;
init.num_parents = 0;
data->hw.init = &init;
data->i2c_client = client;
data->speed_grade = id->driver_data;
if (of_property_read_string(client->dev.of_node, "clock-output-names",
&init.name))
init.name = client->dev.of_node->name;
data->regmap = devm_regmap_init_i2c(client, &si544_regmap_config);
if (IS_ERR(data->regmap))
return PTR_ERR(data->regmap);
i2c_set_clientdata(client, data);
/* Select page 0, just to be sure, there appear to be no more */
err = regmap_write(data->regmap, SI544_REG_PAGE_SELECT, 0);
if (err < 0)
return err;
err = devm_clk_hw_register(&client->dev, &data->hw);
if (err) {
dev_err(&client->dev, "clock registration failed\n");
return err;
}
err = devm_of_clk_add_hw_provider(&client->dev, of_clk_hw_simple_get,
&data->hw);
if (err) {
dev_err(&client->dev, "unable to add clk provider\n");
return err;
}
return 0;
}
static const struct i2c_device_id si544_id[] = {
{ "si544a", si544a },
{ "si544b", si544b },
{ "si544c", si544c },
{ }
};
MODULE_DEVICE_TABLE(i2c, si544_id);
static const struct of_device_id clk_si544_of_match[] = {
{ .compatible = "silabs,si544a" },
{ .compatible = "silabs,si544b" },
{ .compatible = "silabs,si544c" },
{ },
};
MODULE_DEVICE_TABLE(of, clk_si544_of_match);
static struct i2c_driver si544_driver = {
.driver = {
.name = "si544",
.of_match_table = clk_si544_of_match,
},
.probe = si544_probe,
.id_table = si544_id,
};
module_i2c_driver(si544_driver);
MODULE_AUTHOR("Mike Looijmans <mike.looijmans@topic.nl>");
MODULE_DESCRIPTION("Si544 driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,21 @@
# SPDX-License-Identifier: GPL-2.0
ifeq ($(CONFIG_COMMON_CLK), y)
obj-$(CONFIG_ARCH_DAVINCI_DA8XX) += da8xx-cfgchip.o
obj-y += pll.o
obj-$(CONFIG_ARCH_DAVINCI_DA830) += pll-da830.o
obj-$(CONFIG_ARCH_DAVINCI_DA850) += pll-da850.o
obj-$(CONFIG_ARCH_DAVINCI_DM355) += pll-dm355.o
obj-$(CONFIG_ARCH_DAVINCI_DM365) += pll-dm365.o
obj-$(CONFIG_ARCH_DAVINCI_DM644x) += pll-dm644x.o
obj-$(CONFIG_ARCH_DAVINCI_DM646x) += pll-dm646x.o
obj-y += psc.o
obj-$(CONFIG_ARCH_DAVINCI_DA830) += psc-da830.o
obj-$(CONFIG_ARCH_DAVINCI_DA850) += psc-da850.o
obj-$(CONFIG_ARCH_DAVINCI_DM355) += psc-dm355.o
obj-$(CONFIG_ARCH_DAVINCI_DM365) += psc-dm365.o
obj-$(CONFIG_ARCH_DAVINCI_DM644x) += psc-dm644x.o
obj-$(CONFIG_ARCH_DAVINCI_DM646x) += psc-dm646x.o
endif

View File

@ -0,0 +1,790 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Clock driver for DA8xx/AM17xx/AM18xx/OMAP-L13x CFGCHIP
*
* Copyright (C) 2018 David Lechner <david@lechnology.com>
*/
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/init.h>
#include <linux/mfd/da8xx-cfgchip.h>
#include <linux/mfd/syscon.h>
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/platform_data/clk-da8xx-cfgchip.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
/* --- Gate clocks --- */
#define DA8XX_GATE_CLOCK_IS_DIV4P5 BIT(1)
struct da8xx_cfgchip_gate_clk_info {
const char *name;
u32 cfgchip;
u32 bit;
u32 flags;
};
struct da8xx_cfgchip_gate_clk {
struct clk_hw hw;
struct regmap *regmap;
u32 reg;
u32 mask;
};
#define to_da8xx_cfgchip_gate_clk(_hw) \
container_of((_hw), struct da8xx_cfgchip_gate_clk, hw)
static int da8xx_cfgchip_gate_clk_enable(struct clk_hw *hw)
{
struct da8xx_cfgchip_gate_clk *clk = to_da8xx_cfgchip_gate_clk(hw);
return regmap_write_bits(clk->regmap, clk->reg, clk->mask, clk->mask);
}
static void da8xx_cfgchip_gate_clk_disable(struct clk_hw *hw)
{
struct da8xx_cfgchip_gate_clk *clk = to_da8xx_cfgchip_gate_clk(hw);
regmap_write_bits(clk->regmap, clk->reg, clk->mask, 0);
}
static int da8xx_cfgchip_gate_clk_is_enabled(struct clk_hw *hw)
{
struct da8xx_cfgchip_gate_clk *clk = to_da8xx_cfgchip_gate_clk(hw);
unsigned int val;
regmap_read(clk->regmap, clk->reg, &val);
return !!(val & clk->mask);
}
static unsigned long da8xx_cfgchip_div4p5_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
/* this clock divides by 4.5 */
return parent_rate * 2 / 9;
}
static const struct clk_ops da8xx_cfgchip_gate_clk_ops = {
.enable = da8xx_cfgchip_gate_clk_enable,
.disable = da8xx_cfgchip_gate_clk_disable,
.is_enabled = da8xx_cfgchip_gate_clk_is_enabled,
};
static const struct clk_ops da8xx_cfgchip_div4p5_clk_ops = {
.enable = da8xx_cfgchip_gate_clk_enable,
.disable = da8xx_cfgchip_gate_clk_disable,
.is_enabled = da8xx_cfgchip_gate_clk_is_enabled,
.recalc_rate = da8xx_cfgchip_div4p5_recalc_rate,
};
static struct da8xx_cfgchip_gate_clk * __init
da8xx_cfgchip_gate_clk_register(struct device *dev,
const struct da8xx_cfgchip_gate_clk_info *info,
struct regmap *regmap)
{
struct clk *parent;
const char *parent_name;
struct da8xx_cfgchip_gate_clk *gate;
struct clk_init_data init;
int ret;
parent = devm_clk_get(dev, NULL);
if (IS_ERR(parent))
return ERR_CAST(parent);
parent_name = __clk_get_name(parent);
gate = devm_kzalloc(dev, sizeof(*gate), GFP_KERNEL);
if (!gate)
return ERR_PTR(-ENOMEM);
init.name = info->name;
if (info->flags & DA8XX_GATE_CLOCK_IS_DIV4P5)
init.ops = &da8xx_cfgchip_div4p5_clk_ops;
else
init.ops = &da8xx_cfgchip_gate_clk_ops;
init.parent_names = &parent_name;
init.num_parents = 1;
init.flags = 0;
gate->hw.init = &init;
gate->regmap = regmap;
gate->reg = info->cfgchip;
gate->mask = info->bit;
ret = devm_clk_hw_register(dev, &gate->hw);
if (ret < 0)
return ERR_PTR(ret);
return gate;
}
static const struct da8xx_cfgchip_gate_clk_info da8xx_tbclksync_info __initconst = {
.name = "ehrpwm_tbclk",
.cfgchip = CFGCHIP(1),
.bit = CFGCHIP1_TBCLKSYNC,
};
static int __init da8xx_cfgchip_register_tbclk(struct device *dev,
struct regmap *regmap)
{
struct da8xx_cfgchip_gate_clk *gate;
gate = da8xx_cfgchip_gate_clk_register(dev, &da8xx_tbclksync_info,
regmap);
if (IS_ERR(gate))
return PTR_ERR(gate);
clk_hw_register_clkdev(&gate->hw, "tbclk", "ehrpwm.0");
clk_hw_register_clkdev(&gate->hw, "tbclk", "ehrpwm.1");
return 0;
}
static const struct da8xx_cfgchip_gate_clk_info da8xx_div4p5ena_info __initconst = {
.name = "div4.5",
.cfgchip = CFGCHIP(3),
.bit = CFGCHIP3_DIV45PENA,
.flags = DA8XX_GATE_CLOCK_IS_DIV4P5,
};
static int __init da8xx_cfgchip_register_div4p5(struct device *dev,
struct regmap *regmap)
{
struct da8xx_cfgchip_gate_clk *gate;
gate = da8xx_cfgchip_gate_clk_register(dev, &da8xx_div4p5ena_info, regmap);
if (IS_ERR(gate))
return PTR_ERR(gate);
return 0;
}
static int __init
of_da8xx_cfgchip_gate_clk_init(struct device *dev,
const struct da8xx_cfgchip_gate_clk_info *info,
struct regmap *regmap)
{
struct da8xx_cfgchip_gate_clk *gate;
gate = da8xx_cfgchip_gate_clk_register(dev, info, regmap);
if (IS_ERR(gate))
return PTR_ERR(gate);
return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, gate);
}
static int __init of_da8xx_tbclksync_init(struct device *dev,
struct regmap *regmap)
{
return of_da8xx_cfgchip_gate_clk_init(dev, &da8xx_tbclksync_info, regmap);
}
static int __init of_da8xx_div4p5ena_init(struct device *dev,
struct regmap *regmap)
{
return of_da8xx_cfgchip_gate_clk_init(dev, &da8xx_div4p5ena_info, regmap);
}
/* --- MUX clocks --- */
struct da8xx_cfgchip_mux_clk_info {
const char *name;
const char *parent0;
const char *parent1;
u32 cfgchip;
u32 bit;
};
struct da8xx_cfgchip_mux_clk {
struct clk_hw hw;
struct regmap *regmap;
u32 reg;
u32 mask;
};
#define to_da8xx_cfgchip_mux_clk(_hw) \
container_of((_hw), struct da8xx_cfgchip_mux_clk, hw)
static int da8xx_cfgchip_mux_clk_set_parent(struct clk_hw *hw, u8 index)
{
struct da8xx_cfgchip_mux_clk *clk = to_da8xx_cfgchip_mux_clk(hw);
unsigned int val = index ? clk->mask : 0;
return regmap_write_bits(clk->regmap, clk->reg, clk->mask, val);
}
static u8 da8xx_cfgchip_mux_clk_get_parent(struct clk_hw *hw)
{
struct da8xx_cfgchip_mux_clk *clk = to_da8xx_cfgchip_mux_clk(hw);
unsigned int val;
regmap_read(clk->regmap, clk->reg, &val);
return (val & clk->mask) ? 1 : 0;
}
static const struct clk_ops da8xx_cfgchip_mux_clk_ops = {
.set_parent = da8xx_cfgchip_mux_clk_set_parent,
.get_parent = da8xx_cfgchip_mux_clk_get_parent,
};
static struct da8xx_cfgchip_mux_clk * __init
da8xx_cfgchip_mux_clk_register(struct device *dev,
const struct da8xx_cfgchip_mux_clk_info *info,
struct regmap *regmap)
{
const char * const parent_names[] = { info->parent0, info->parent1 };
struct da8xx_cfgchip_mux_clk *mux;
struct clk_init_data init;
int ret;
mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
if (!mux)
return ERR_PTR(-ENOMEM);
init.name = info->name;
init.ops = &da8xx_cfgchip_mux_clk_ops;
init.parent_names = parent_names;
init.num_parents = 2;
init.flags = 0;
mux->hw.init = &init;
mux->regmap = regmap;
mux->reg = info->cfgchip;
mux->mask = info->bit;
ret = devm_clk_hw_register(dev, &mux->hw);
if (ret < 0)
return ERR_PTR(ret);
return mux;
}
static const struct da8xx_cfgchip_mux_clk_info da850_async1_info __initconst = {
.name = "async1",
.parent0 = "pll0_sysclk3",
.parent1 = "div4.5",
.cfgchip = CFGCHIP(3),
.bit = CFGCHIP3_EMA_CLKSRC,
};
static int __init da8xx_cfgchip_register_async1(struct device *dev,
struct regmap *regmap)
{
struct da8xx_cfgchip_mux_clk *mux;
mux = da8xx_cfgchip_mux_clk_register(dev, &da850_async1_info, regmap);
if (IS_ERR(mux))
return PTR_ERR(mux);
clk_hw_register_clkdev(&mux->hw, "async1", "da850-psc0");
return 0;
}
static const struct da8xx_cfgchip_mux_clk_info da850_async3_info __initconst = {
.name = "async3",
.parent0 = "pll0_sysclk2",
.parent1 = "pll1_sysclk2",
.cfgchip = CFGCHIP(3),
.bit = CFGCHIP3_ASYNC3_CLKSRC,
};
static int __init da850_cfgchip_register_async3(struct device *dev,
struct regmap *regmap)
{
struct da8xx_cfgchip_mux_clk *mux;
struct clk_hw *parent;
mux = da8xx_cfgchip_mux_clk_register(dev, &da850_async3_info, regmap);
if (IS_ERR(mux))
return PTR_ERR(mux);
clk_hw_register_clkdev(&mux->hw, "async3", "da850-psc1");
/* pll1_sysclk2 is not affected by CPU scaling, so use it for async3 */
parent = clk_hw_get_parent_by_index(&mux->hw, 1);
if (parent)
clk_set_parent(mux->hw.clk, parent->clk);
else
dev_warn(dev, "Failed to find async3 parent clock\n");
return 0;
}
static int __init
of_da8xx_cfgchip_init_mux_clock(struct device *dev,
const struct da8xx_cfgchip_mux_clk_info *info,
struct regmap *regmap)
{
struct da8xx_cfgchip_mux_clk *mux;
mux = da8xx_cfgchip_mux_clk_register(dev, info, regmap);
if (IS_ERR(mux))
return PTR_ERR(mux);
return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &mux->hw);
}
static int __init of_da850_async1_init(struct device *dev, struct regmap *regmap)
{
return of_da8xx_cfgchip_init_mux_clock(dev, &da850_async1_info, regmap);
}
static int __init of_da850_async3_init(struct device *dev, struct regmap *regmap)
{
return of_da8xx_cfgchip_init_mux_clock(dev, &da850_async3_info, regmap);
}
/* --- USB 2.0 PHY clock --- */
struct da8xx_usb0_clk48 {
struct clk_hw hw;
struct clk *fck;
struct regmap *regmap;
};
#define to_da8xx_usb0_clk48(_hw) \
container_of((_hw), struct da8xx_usb0_clk48, hw)
static int da8xx_usb0_clk48_prepare(struct clk_hw *hw)
{
struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw);
/* The USB 2.0 PSC clock is only needed temporarily during the USB 2.0
* PHY clock enable, but since clk_prepare() can't be called in an
* atomic context (i.e. in clk_enable()), we have to prepare it here.
*/
return clk_prepare(usb0->fck);
}
static void da8xx_usb0_clk48_unprepare(struct clk_hw *hw)
{
struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw);
clk_unprepare(usb0->fck);
}
static int da8xx_usb0_clk48_enable(struct clk_hw *hw)
{
struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw);
unsigned int mask, val;
int ret;
/* Locking the USB 2.O PLL requires that the USB 2.O PSC is enabled
* temporaily. It can be turned back off once the PLL is locked.
*/
clk_enable(usb0->fck);
/* Turn on the USB 2.0 PHY, but just the PLL, and not OTG. The USB 1.1
* PHY may use the USB 2.0 PLL clock without USB 2.0 OTG being used.
*/
mask = CFGCHIP2_RESET | CFGCHIP2_PHYPWRDN | CFGCHIP2_PHY_PLLON;
val = CFGCHIP2_PHY_PLLON;
regmap_write_bits(usb0->regmap, CFGCHIP(2), mask, val);
ret = regmap_read_poll_timeout(usb0->regmap, CFGCHIP(2), val,
val & CFGCHIP2_PHYCLKGD, 0, 500000);
clk_disable(usb0->fck);
return ret;
}
static void da8xx_usb0_clk48_disable(struct clk_hw *hw)
{
struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw);
unsigned int val;
val = CFGCHIP2_PHYPWRDN;
regmap_write_bits(usb0->regmap, CFGCHIP(2), val, val);
}
static int da8xx_usb0_clk48_is_enabled(struct clk_hw *hw)
{
struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw);
unsigned int val;
regmap_read(usb0->regmap, CFGCHIP(2), &val);
return !!(val & CFGCHIP2_PHYCLKGD);
}
static unsigned long da8xx_usb0_clk48_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw);
unsigned int mask, val;
/* The parent clock rate must be one of the following */
mask = CFGCHIP2_REFFREQ_MASK;
switch (parent_rate) {
case 12000000:
val = CFGCHIP2_REFFREQ_12MHZ;
break;
case 13000000:
val = CFGCHIP2_REFFREQ_13MHZ;
break;
case 19200000:
val = CFGCHIP2_REFFREQ_19_2MHZ;
break;
case 20000000:
val = CFGCHIP2_REFFREQ_20MHZ;
break;
case 24000000:
val = CFGCHIP2_REFFREQ_24MHZ;
break;
case 26000000:
val = CFGCHIP2_REFFREQ_26MHZ;
break;
case 38400000:
val = CFGCHIP2_REFFREQ_38_4MHZ;
break;
case 40000000:
val = CFGCHIP2_REFFREQ_40MHZ;
break;
case 48000000:
val = CFGCHIP2_REFFREQ_48MHZ;
break;
default:
return 0;
}
regmap_write_bits(usb0->regmap, CFGCHIP(2), mask, val);
/* USB 2.0 PLL always supplies 48MHz */
return 48000000;
}
static long da8xx_usb0_clk48_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
return 48000000;
}
static int da8xx_usb0_clk48_set_parent(struct clk_hw *hw, u8 index)
{
struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw);
return regmap_write_bits(usb0->regmap, CFGCHIP(2),
CFGCHIP2_USB2PHYCLKMUX,
index ? CFGCHIP2_USB2PHYCLKMUX : 0);
}
static u8 da8xx_usb0_clk48_get_parent(struct clk_hw *hw)
{
struct da8xx_usb0_clk48 *usb0 = to_da8xx_usb0_clk48(hw);
unsigned int val;
regmap_read(usb0->regmap, CFGCHIP(2), &val);
return (val & CFGCHIP2_USB2PHYCLKMUX) ? 1 : 0;
}
static const struct clk_ops da8xx_usb0_clk48_ops = {
.prepare = da8xx_usb0_clk48_prepare,
.unprepare = da8xx_usb0_clk48_unprepare,
.enable = da8xx_usb0_clk48_enable,
.disable = da8xx_usb0_clk48_disable,
.is_enabled = da8xx_usb0_clk48_is_enabled,
.recalc_rate = da8xx_usb0_clk48_recalc_rate,
.round_rate = da8xx_usb0_clk48_round_rate,
.set_parent = da8xx_usb0_clk48_set_parent,
.get_parent = da8xx_usb0_clk48_get_parent,
};
static struct da8xx_usb0_clk48 *
da8xx_cfgchip_register_usb0_clk48(struct device *dev,
struct regmap *regmap)
{
const char * const parent_names[] = { "usb_refclkin", "pll0_auxclk" };
struct clk *fck_clk;
struct da8xx_usb0_clk48 *usb0;
struct clk_init_data init;
int ret;
fck_clk = devm_clk_get(dev, "fck");
if (IS_ERR(fck_clk)) {
if (PTR_ERR(fck_clk) != -EPROBE_DEFER)
dev_err(dev, "Missing fck clock\n");
return ERR_CAST(fck_clk);
}
usb0 = devm_kzalloc(dev, sizeof(*usb0), GFP_KERNEL);
if (!usb0)
return ERR_PTR(-ENOMEM);
init.name = "usb0_clk48";
init.ops = &da8xx_usb0_clk48_ops;
init.parent_names = parent_names;
init.num_parents = 2;
usb0->hw.init = &init;
usb0->fck = fck_clk;
usb0->regmap = regmap;
ret = devm_clk_hw_register(dev, &usb0->hw);
if (ret < 0)
return ERR_PTR(ret);
return usb0;
}
/* --- USB 1.1 PHY clock --- */
struct da8xx_usb1_clk48 {
struct clk_hw hw;
struct regmap *regmap;
};
#define to_da8xx_usb1_clk48(_hw) \
container_of((_hw), struct da8xx_usb1_clk48, hw)
static int da8xx_usb1_clk48_set_parent(struct clk_hw *hw, u8 index)
{
struct da8xx_usb1_clk48 *usb1 = to_da8xx_usb1_clk48(hw);
return regmap_write_bits(usb1->regmap, CFGCHIP(2),
CFGCHIP2_USB1PHYCLKMUX,
index ? CFGCHIP2_USB1PHYCLKMUX : 0);
}
static u8 da8xx_usb1_clk48_get_parent(struct clk_hw *hw)
{
struct da8xx_usb1_clk48 *usb1 = to_da8xx_usb1_clk48(hw);
unsigned int val;
regmap_read(usb1->regmap, CFGCHIP(2), &val);
return (val & CFGCHIP2_USB1PHYCLKMUX) ? 1 : 0;
}
static const struct clk_ops da8xx_usb1_clk48_ops = {
.set_parent = da8xx_usb1_clk48_set_parent,
.get_parent = da8xx_usb1_clk48_get_parent,
};
/**
* da8xx_cfgchip_register_usb1_clk48 - Register a new USB 1.1 PHY clock
* @regmap: The CFGCHIP regmap
*/
static struct da8xx_usb1_clk48 *
da8xx_cfgchip_register_usb1_clk48(struct device *dev,
struct regmap *regmap)
{
const char * const parent_names[] = { "usb0_clk48", "usb_refclkin" };
struct da8xx_usb1_clk48 *usb1;
struct clk_init_data init;
int ret;
usb1 = devm_kzalloc(dev, sizeof(*usb1), GFP_KERNEL);
if (!usb1)
return ERR_PTR(-ENOMEM);
init.name = "usb1_clk48";
init.ops = &da8xx_usb1_clk48_ops;
init.parent_names = parent_names;
init.num_parents = 2;
usb1->hw.init = &init;
usb1->regmap = regmap;
ret = devm_clk_hw_register(dev, &usb1->hw);
if (ret < 0)
return ERR_PTR(ret);
return usb1;
}
static int da8xx_cfgchip_register_usb_phy_clk(struct device *dev,
struct regmap *regmap)
{
struct da8xx_usb0_clk48 *usb0;
struct da8xx_usb1_clk48 *usb1;
struct clk_hw *parent;
usb0 = da8xx_cfgchip_register_usb0_clk48(dev, regmap);
if (IS_ERR(usb0))
return PTR_ERR(usb0);
/*
* All existing boards use pll0_auxclk as the parent and new boards
* should use device tree, so hard-coding the value (1) here.
*/
parent = clk_hw_get_parent_by_index(&usb0->hw, 1);
if (parent)
clk_set_parent(usb0->hw.clk, parent->clk);
else
dev_warn(dev, "Failed to find usb0 parent clock\n");
usb1 = da8xx_cfgchip_register_usb1_clk48(dev, regmap);
if (IS_ERR(usb1))
return PTR_ERR(usb1);
/*
* All existing boards use usb0_clk48 as the parent and new boards
* should use device tree, so hard-coding the value (0) here.
*/
parent = clk_hw_get_parent_by_index(&usb1->hw, 0);
if (parent)
clk_set_parent(usb1->hw.clk, parent->clk);
else
dev_warn(dev, "Failed to find usb1 parent clock\n");
clk_hw_register_clkdev(&usb0->hw, "usb0_clk48", "da8xx-usb-phy");
clk_hw_register_clkdev(&usb1->hw, "usb1_clk48", "da8xx-usb-phy");
return 0;
}
static int of_da8xx_usb_phy_clk_init(struct device *dev, struct regmap *regmap)
{
struct clk_hw_onecell_data *clk_data;
struct da8xx_usb0_clk48 *usb0;
struct da8xx_usb1_clk48 *usb1;
clk_data = devm_kzalloc(dev, sizeof(*clk_data) + 2 *
sizeof(*clk_data->hws), GFP_KERNEL);
if (!clk_data)
return -ENOMEM;
clk_data->num = 2;
usb0 = da8xx_cfgchip_register_usb0_clk48(dev, regmap);
if (IS_ERR(usb0)) {
if (PTR_ERR(usb0) == -EPROBE_DEFER)
return -EPROBE_DEFER;
dev_warn(dev, "Failed to register usb0_clk48 (%ld)\n",
PTR_ERR(usb0));
clk_data->hws[0] = ERR_PTR(-ENOENT);
} else {
clk_data->hws[0] = &usb0->hw;
}
usb1 = da8xx_cfgchip_register_usb1_clk48(dev, regmap);
if (IS_ERR(usb1)) {
if (PTR_ERR(usb0) == -EPROBE_DEFER)
return -EPROBE_DEFER;
dev_warn(dev, "Failed to register usb1_clk48 (%ld)\n",
PTR_ERR(usb1));
clk_data->hws[1] = ERR_PTR(-ENOENT);
} else {
clk_data->hws[1] = &usb1->hw;
}
return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data);
}
/* --- platform device --- */
static const struct of_device_id da8xx_cfgchip_of_match[] = {
{
.compatible = "ti,da830-tbclksync",
.data = of_da8xx_tbclksync_init,
},
{
.compatible = "ti,da830-div4p5ena",
.data = of_da8xx_div4p5ena_init,
},
{
.compatible = "ti,da850-async1-clksrc",
.data = of_da850_async1_init,
},
{
.compatible = "ti,da850-async3-clksrc",
.data = of_da850_async3_init,
},
{
.compatible = "ti,da830-usb-phy-clocks",
.data = of_da8xx_usb_phy_clk_init,
},
{ }
};
static const struct platform_device_id da8xx_cfgchip_id_table[] = {
{
.name = "da830-tbclksync",
.driver_data = (kernel_ulong_t)da8xx_cfgchip_register_tbclk,
},
{
.name = "da830-div4p5ena",
.driver_data = (kernel_ulong_t)da8xx_cfgchip_register_div4p5,
},
{
.name = "da850-async1-clksrc",
.driver_data = (kernel_ulong_t)da8xx_cfgchip_register_async1,
},
{
.name = "da850-async3-clksrc",
.driver_data = (kernel_ulong_t)da850_cfgchip_register_async3,
},
{
.name = "da830-usb-phy-clks",
.driver_data = (kernel_ulong_t)da8xx_cfgchip_register_usb_phy_clk,
},
{ }
};
typedef int (*da8xx_cfgchip_init)(struct device *dev, struct regmap *regmap);
static int da8xx_cfgchip_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct da8xx_cfgchip_clk_platform_data *pdata = dev->platform_data;
const struct of_device_id *of_id;
da8xx_cfgchip_init clk_init = NULL;
struct regmap *regmap = NULL;
of_id = of_match_device(da8xx_cfgchip_of_match, dev);
if (of_id) {
struct device_node *parent;
clk_init = of_id->data;
parent = of_get_parent(dev->of_node);
regmap = syscon_node_to_regmap(parent);
of_node_put(parent);
} else if (pdev->id_entry && pdata) {
clk_init = (void *)pdev->id_entry->driver_data;
regmap = pdata->cfgchip;
}
if (!clk_init) {
dev_err(dev, "unable to find driver data\n");
return -EINVAL;
}
if (IS_ERR_OR_NULL(regmap)) {
dev_err(dev, "no regmap for CFGCHIP syscon\n");
return regmap ? PTR_ERR(regmap) : -ENOENT;
}
return clk_init(dev, regmap);
}
static struct platform_driver da8xx_cfgchip_driver = {
.probe = da8xx_cfgchip_probe,
.driver = {
.name = "da8xx-cfgchip-clk",
.of_match_table = da8xx_cfgchip_of_match,
},
.id_table = da8xx_cfgchip_id_table,
};
static int __init da8xx_cfgchip_driver_init(void)
{
return platform_driver_register(&da8xx_cfgchip_driver);
}
/* has to be postcore_initcall because PSC devices depend on the async3 clock */
postcore_initcall(da8xx_cfgchip_driver_init);

View File

@ -0,0 +1,70 @@
// SPDX-License-Identifier: GPL-2.0
/*
* PLL clock descriptions for TI DA830/OMAP-L137/AM17XX
*
* Copyright (C) 2018 David Lechner <david@lechnology.com>
*/
#include <linux/clkdev.h>
#include <linux/bitops.h>
#include <linux/init.h>
#include <linux/types.h>
#include "pll.h"
static const struct davinci_pll_clk_info da830_pll_info = {
.name = "pll0",
.pllm_mask = GENMASK(4, 0),
.pllm_min = 4,
.pllm_max = 32,
.pllout_min_rate = 300000000,
.pllout_max_rate = 600000000,
.flags = PLL_HAS_CLKMODE | PLL_HAS_PREDIV | PLL_HAS_POSTDIV,
};
/*
* NB: Technically, the clocks flagged as SYSCLK_FIXED_DIV are "fixed ratio",
* meaning that we could change the divider as long as we keep the correct
* ratio between all of the clocks, but we don't support that because there is
* currently not a need for it.
*/
SYSCLK(2, pll0_sysclk2, pll0_pllen, 5, SYSCLK_FIXED_DIV);
SYSCLK(3, pll0_sysclk3, pll0_pllen, 5, 0);
SYSCLK(4, pll0_sysclk4, pll0_pllen, 5, SYSCLK_FIXED_DIV);
SYSCLK(5, pll0_sysclk5, pll0_pllen, 5, 0);
SYSCLK(6, pll0_sysclk6, pll0_pllen, 5, SYSCLK_FIXED_DIV);
SYSCLK(7, pll0_sysclk7, pll0_pllen, 5, 0);
int da830_pll_init(struct device *dev, void __iomem *base)
{
struct clk *clk;
davinci_pll_clk_register(dev, &da830_pll_info, "ref_clk", base);
clk = davinci_pll_sysclk_register(dev, &pll0_sysclk2, base);
clk_register_clkdev(clk, "pll0_sysclk2", "da830-psc0");
clk_register_clkdev(clk, "pll0_sysclk2", "da830-psc1");
clk = davinci_pll_sysclk_register(dev, &pll0_sysclk3, base);
clk_register_clkdev(clk, "pll0_sysclk3", "da830-psc0");
clk = davinci_pll_sysclk_register(dev, &pll0_sysclk4, base);
clk_register_clkdev(clk, "pll0_sysclk4", "da830-psc0");
clk_register_clkdev(clk, "pll0_sysclk4", "da830-psc1");
clk = davinci_pll_sysclk_register(dev, &pll0_sysclk5, base);
clk_register_clkdev(clk, "pll0_sysclk5", "da830-psc1");
clk = davinci_pll_sysclk_register(dev, &pll0_sysclk6, base);
clk_register_clkdev(clk, "pll0_sysclk6", "da830-psc0");
clk = davinci_pll_sysclk_register(dev, &pll0_sysclk7, base);
clk = davinci_pll_auxclk_register(dev, "pll0_auxclk", base);
clk_register_clkdev(clk, NULL, "i2c_davinci.1");
clk_register_clkdev(clk, "timer0", NULL);
clk_register_clkdev(clk, NULL, "davinci-wdt");
return 0;
}

View File

@ -0,0 +1,212 @@
// SPDX-License-Identifier: GPL-2.0
/*
* PLL clock descriptions for TI DA850/OMAP-L138/AM18XX
*
* Copyright (C) 2018 David Lechner <david@lechnology.com>
*/
#include <linux/bitops.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/mfd/da8xx-cfgchip.h>
#include <linux/of.h>
#include <linux/types.h>
#include "pll.h"
#define OCSEL_OCSRC_OSCIN 0x14
#define OCSEL_OCSRC_PLL0_SYSCLK(n) (0x16 + (n))
#define OCSEL_OCSRC_PLL1_OBSCLK 0x1e
#define OCSEL_OCSRC_PLL1_SYSCLK(n) (0x16 + (n))
static const struct davinci_pll_clk_info da850_pll0_info = {
.name = "pll0",
.unlock_reg = CFGCHIP(0),
.unlock_mask = CFGCHIP0_PLL_MASTER_LOCK,
.pllm_mask = GENMASK(4, 0),
.pllm_min = 4,
.pllm_max = 32,
.pllout_min_rate = 300000000,
.pllout_max_rate = 600000000,
.flags = PLL_HAS_CLKMODE | PLL_HAS_PREDIV | PLL_HAS_POSTDIV |
PLL_HAS_EXTCLKSRC,
};
/*
* NB: Technically, the clocks flagged as SYSCLK_FIXED_DIV are "fixed ratio",
* meaning that we could change the divider as long as we keep the correct
* ratio between all of the clocks, but we don't support that because there is
* currently not a need for it.
*/
SYSCLK(1, pll0_sysclk1, pll0_pllen, 5, SYSCLK_FIXED_DIV);
SYSCLK(2, pll0_sysclk2, pll0_pllen, 5, SYSCLK_FIXED_DIV);
SYSCLK(3, pll0_sysclk3, pll0_pllen, 5, 0);
SYSCLK(4, pll0_sysclk4, pll0_pllen, 5, SYSCLK_FIXED_DIV);
SYSCLK(5, pll0_sysclk5, pll0_pllen, 5, 0);
SYSCLK(6, pll0_sysclk6, pll0_pllen, 5, SYSCLK_ARM_RATE | SYSCLK_FIXED_DIV);
SYSCLK(7, pll0_sysclk7, pll0_pllen, 5, 0);
static const char * const da850_pll0_obsclk_parent_names[] = {
"oscin",
"pll0_sysclk1",
"pll0_sysclk2",
"pll0_sysclk3",
"pll0_sysclk4",
"pll0_sysclk5",
"pll0_sysclk6",
"pll0_sysclk7",
"pll1_obsclk",
};
static u32 da850_pll0_obsclk_table[] = {
OCSEL_OCSRC_OSCIN,
OCSEL_OCSRC_PLL0_SYSCLK(1),
OCSEL_OCSRC_PLL0_SYSCLK(2),
OCSEL_OCSRC_PLL0_SYSCLK(3),
OCSEL_OCSRC_PLL0_SYSCLK(4),
OCSEL_OCSRC_PLL0_SYSCLK(5),
OCSEL_OCSRC_PLL0_SYSCLK(6),
OCSEL_OCSRC_PLL0_SYSCLK(7),
OCSEL_OCSRC_PLL1_OBSCLK,
};
static const struct davinci_pll_obsclk_info da850_pll0_obsclk_info = {
.name = "pll0_obsclk",
.parent_names = da850_pll0_obsclk_parent_names,
.num_parents = ARRAY_SIZE(da850_pll0_obsclk_parent_names),
.table = da850_pll0_obsclk_table,
.ocsrc_mask = GENMASK(4, 0),
};
int da850_pll0_init(struct device *dev, void __iomem *base)
{
struct clk *clk;
davinci_pll_clk_register(dev, &da850_pll0_info, "ref_clk", base);
clk = davinci_pll_sysclk_register(dev, &pll0_sysclk1, base);
clk_register_clkdev(clk, "pll0_sysclk1", "da850-psc0");
clk = davinci_pll_sysclk_register(dev, &pll0_sysclk2, base);
clk_register_clkdev(clk, "pll0_sysclk2", "da850-psc0");
clk_register_clkdev(clk, "pll0_sysclk2", "da850-psc1");
clk_register_clkdev(clk, "pll0_sysclk2", "da850-async3-clksrc");
clk = davinci_pll_sysclk_register(dev, &pll0_sysclk3, base);
clk_register_clkdev(clk, "pll0_sysclk3", "da850-async1-clksrc");
clk = davinci_pll_sysclk_register(dev, &pll0_sysclk4, base);
clk_register_clkdev(clk, "pll0_sysclk4", "da850-psc0");
clk_register_clkdev(clk, "pll0_sysclk4", "da850-psc1");
davinci_pll_sysclk_register(dev, &pll0_sysclk5, base);
clk = davinci_pll_sysclk_register(dev, &pll0_sysclk6, base);
clk_register_clkdev(clk, "pll0_sysclk6", "da850-psc0");
davinci_pll_sysclk_register(dev, &pll0_sysclk7, base);
davinci_pll_auxclk_register(dev, "pll0_auxclk", base);
clk = clk_register_fixed_factor(dev, "async2", "pll0_auxclk",
CLK_IS_CRITICAL, 1, 1);
clk_register_clkdev(clk, NULL, "i2c_davinci.1");
clk_register_clkdev(clk, "timer0", NULL);
clk_register_clkdev(clk, NULL, "davinci-wdt");
davinci_pll_obsclk_register(dev, &da850_pll0_obsclk_info, base);
return 0;
}
static const struct davinci_pll_sysclk_info *da850_pll0_sysclk_info[] = {
&pll0_sysclk1,
&pll0_sysclk2,
&pll0_sysclk3,
&pll0_sysclk4,
&pll0_sysclk5,
&pll0_sysclk6,
&pll0_sysclk7,
NULL
};
int of_da850_pll0_init(struct device *dev, void __iomem *base)
{
return of_davinci_pll_init(dev, &da850_pll0_info,
&da850_pll0_obsclk_info,
da850_pll0_sysclk_info, 7, base);
}
static const struct davinci_pll_clk_info da850_pll1_info = {
.name = "pll1",
.unlock_reg = CFGCHIP(3),
.unlock_mask = CFGCHIP3_PLL1_MASTER_LOCK,
.pllm_mask = GENMASK(4, 0),
.pllm_min = 4,
.pllm_max = 32,
.pllout_min_rate = 300000000,
.pllout_max_rate = 600000000,
.flags = PLL_HAS_POSTDIV,
};
SYSCLK(1, pll1_sysclk1, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED);
SYSCLK(2, pll1_sysclk2, pll1_pllen, 5, 0);
SYSCLK(3, pll1_sysclk3, pll1_pllen, 5, 0);
static const char * const da850_pll1_obsclk_parent_names[] = {
"oscin",
"pll1_sysclk1",
"pll1_sysclk2",
"pll1_sysclk3",
};
static u32 da850_pll1_obsclk_table[] = {
OCSEL_OCSRC_OSCIN,
OCSEL_OCSRC_PLL1_SYSCLK(1),
OCSEL_OCSRC_PLL1_SYSCLK(2),
OCSEL_OCSRC_PLL1_SYSCLK(3),
};
static const struct davinci_pll_obsclk_info da850_pll1_obsclk_info = {
.name = "pll1_obsclk",
.parent_names = da850_pll1_obsclk_parent_names,
.num_parents = ARRAY_SIZE(da850_pll1_obsclk_parent_names),
.table = da850_pll1_obsclk_table,
.ocsrc_mask = GENMASK(4, 0),
};
int da850_pll1_init(struct device *dev, void __iomem *base)
{
struct clk *clk;
davinci_pll_clk_register(dev, &da850_pll1_info, "oscin", base);
davinci_pll_sysclk_register(dev, &pll1_sysclk1, base);
clk = davinci_pll_sysclk_register(dev, &pll1_sysclk2, base);
clk_register_clkdev(clk, "pll1_sysclk2", "da850-async3-clksrc");
davinci_pll_sysclk_register(dev, &pll1_sysclk3, base);
davinci_pll_obsclk_register(dev, &da850_pll1_obsclk_info, base);
return 0;
}
static const struct davinci_pll_sysclk_info *da850_pll1_sysclk_info[] = {
&pll1_sysclk1,
&pll1_sysclk2,
&pll1_sysclk3,
NULL
};
int of_da850_pll1_init(struct device *dev, void __iomem *base)
{
return of_davinci_pll_init(dev, &da850_pll1_info,
&da850_pll1_obsclk_info,
da850_pll1_sysclk_info, 3, base);
}

View File

@ -0,0 +1,79 @@
// SPDX-License-Identifier: GPL-2.0
/*
* PLL clock descriptions for TI DM355
*
* Copyright (C) 2018 David Lechner <david@lechnology.com>
*/
#include <linux/bitops.h>
#include <linux/clkdev.h>
#include <linux/init.h>
#include <linux/types.h>
#include "pll.h"
static const struct davinci_pll_clk_info dm355_pll1_info = {
.name = "pll1",
.pllm_mask = GENMASK(7, 0),
.pllm_min = 92,
.pllm_max = 184,
.flags = PLL_HAS_CLKMODE | PLL_HAS_PREDIV | PLL_PREDIV_ALWAYS_ENABLED |
PLL_PREDIV_FIXED8 | PLL_HAS_POSTDIV |
PLL_POSTDIV_ALWAYS_ENABLED | PLL_POSTDIV_FIXED_DIV,
};
SYSCLK(1, pll1_sysclk1, pll1, 5, SYSCLK_FIXED_DIV | SYSCLK_ALWAYS_ENABLED);
SYSCLK(2, pll1_sysclk2, pll1, 5, SYSCLK_FIXED_DIV | SYSCLK_ALWAYS_ENABLED);
SYSCLK(3, pll1_sysclk3, pll1, 5, SYSCLK_ALWAYS_ENABLED);
SYSCLK(4, pll1_sysclk4, pll1, 5, SYSCLK_ALWAYS_ENABLED);
int dm355_pll1_init(struct device *dev, void __iomem *base)
{
struct clk *clk;
davinci_pll_clk_register(dev, &dm355_pll1_info, "ref_clk", base);
clk = davinci_pll_sysclk_register(dev, &pll1_sysclk1, base);
clk_register_clkdev(clk, "pll1_sysclk1", "dm355-psc");
clk = davinci_pll_sysclk_register(dev, &pll1_sysclk2, base);
clk_register_clkdev(clk, "pll1_sysclk2", "dm355-psc");
clk = davinci_pll_sysclk_register(dev, &pll1_sysclk3, base);
clk_register_clkdev(clk, "pll1_sysclk3", "dm355-psc");
clk = davinci_pll_sysclk_register(dev, &pll1_sysclk4, base);
clk_register_clkdev(clk, "pll1_sysclk4", "dm355-psc");
clk = davinci_pll_auxclk_register(dev, "pll1_auxclk", base);
clk_register_clkdev(clk, "pll1_auxclk", "dm355-psc");
davinci_pll_sysclkbp_clk_register(dev, "pll1_sysclkbp", base);
return 0;
}
static const struct davinci_pll_clk_info dm355_pll2_info = {
.name = "pll2",
.pllm_mask = GENMASK(7, 0),
.pllm_min = 92,
.pllm_max = 184,
.flags = PLL_HAS_PREDIV | PLL_PREDIV_ALWAYS_ENABLED | PLL_HAS_POSTDIV |
PLL_POSTDIV_ALWAYS_ENABLED | PLL_POSTDIV_FIXED_DIV,
};
SYSCLK(1, pll2_sysclk1, pll2, 5, SYSCLK_FIXED_DIV);
SYSCLK(2, pll2_sysclk2, pll2, 5, SYSCLK_FIXED_DIV | SYSCLK_ALWAYS_ENABLED);
int dm355_pll2_init(struct device *dev, void __iomem *base)
{
davinci_pll_clk_register(dev, &dm355_pll2_info, "oscin", base);
davinci_pll_sysclk_register(dev, &pll2_sysclk1, base);
davinci_pll_sysclk_register(dev, &pll2_sysclk2, base);
davinci_pll_sysclkbp_clk_register(dev, "pll2_sysclkbp", base);
return 0;
}

View File

@ -0,0 +1,145 @@
// SPDX-License-Identifier: GPL-2.0
/*
* PLL clock descriptions for TI DM365
*
* Copyright (C) 2018 David Lechner <david@lechnology.com>
*/
#include <linux/bitops.h>
#include <linux/clkdev.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include "pll.h"
#define OCSEL_OCSRC_ENABLE 0
static const struct davinci_pll_clk_info dm365_pll1_info = {
.name = "pll1",
.pllm_mask = GENMASK(9, 0),
.pllm_min = 1,
.pllm_max = 1023,
.flags = PLL_HAS_CLKMODE | PLL_HAS_PREDIV | PLL_HAS_POSTDIV |
PLL_POSTDIV_ALWAYS_ENABLED | PLL_PLLM_2X,
};
SYSCLK(1, pll1_sysclk1, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED);
SYSCLK(2, pll1_sysclk2, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED);
SYSCLK(3, pll1_sysclk3, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED);
SYSCLK(4, pll1_sysclk4, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED);
SYSCLK(5, pll1_sysclk5, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED);
SYSCLK(6, pll1_sysclk6, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED);
SYSCLK(7, pll1_sysclk7, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED);
SYSCLK(8, pll1_sysclk8, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED);
SYSCLK(9, pll1_sysclk9, pll1_pllen, 5, SYSCLK_ALWAYS_ENABLED);
/*
* This is a bit of a hack to make OCSEL[OCSRC] on DM365 look like OCSEL[OCSRC]
* on DA850. On DM365, OCSEL[OCSRC] is just an enable/disable bit instead of a
* multiplexer. By modeling it as a single parent mux clock, the clock code will
* still do the right thing in this case.
*/
static const char * const dm365_pll_obsclk_parent_names[] = {
"oscin",
};
static u32 dm365_pll_obsclk_table[] = {
OCSEL_OCSRC_ENABLE,
};
static const struct davinci_pll_obsclk_info dm365_pll1_obsclk_info = {
.name = "pll1_obsclk",
.parent_names = dm365_pll_obsclk_parent_names,
.num_parents = ARRAY_SIZE(dm365_pll_obsclk_parent_names),
.table = dm365_pll_obsclk_table,
.ocsrc_mask = BIT(4),
};
int dm365_pll1_init(struct device *dev, void __iomem *base)
{
struct clk *clk;
davinci_pll_clk_register(dev, &dm365_pll1_info, "ref_clk", base);
clk = davinci_pll_sysclk_register(dev, &pll1_sysclk1, base);
clk_register_clkdev(clk, "pll1_sysclk1", "dm365-psc");
clk = davinci_pll_sysclk_register(dev, &pll1_sysclk2, base);
clk_register_clkdev(clk, "pll1_sysclk2", "dm365-psc");
clk = davinci_pll_sysclk_register(dev, &pll1_sysclk3, base);
clk_register_clkdev(clk, "pll1_sysclk3", "dm365-psc");
clk = davinci_pll_sysclk_register(dev, &pll1_sysclk4, base);
clk_register_clkdev(clk, "pll1_sysclk4", "dm365-psc");
clk = davinci_pll_sysclk_register(dev, &pll1_sysclk5, base);
clk_register_clkdev(clk, "pll1_sysclk5", "dm365-psc");
davinci_pll_sysclk_register(dev, &pll1_sysclk6, base);
davinci_pll_sysclk_register(dev, &pll1_sysclk7, base);
clk = davinci_pll_sysclk_register(dev, &pll1_sysclk8, base);
clk_register_clkdev(clk, "pll1_sysclk8", "dm365-psc");
davinci_pll_sysclk_register(dev, &pll1_sysclk9, base);
clk = davinci_pll_auxclk_register(dev, "pll1_auxclk", base);
clk_register_clkdev(clk, "pll1_auxclk", "dm355-psc");
davinci_pll_sysclkbp_clk_register(dev, "pll1_sysclkbp", base);
davinci_pll_obsclk_register(dev, &dm365_pll1_obsclk_info, base);
return 0;
}
static const struct davinci_pll_clk_info dm365_pll2_info = {
.name = "pll2",
.pllm_mask = GENMASK(9, 0),
.pllm_min = 1,
.pllm_max = 1023,
.flags = PLL_HAS_PREDIV | PLL_HAS_POSTDIV | PLL_POSTDIV_ALWAYS_ENABLED |
PLL_PLLM_2X,
};
SYSCLK(1, pll2_sysclk1, pll2_pllen, 5, SYSCLK_ALWAYS_ENABLED);
SYSCLK(2, pll2_sysclk2, pll2_pllen, 5, SYSCLK_ALWAYS_ENABLED);
SYSCLK(3, pll2_sysclk3, pll2_pllen, 5, SYSCLK_ALWAYS_ENABLED);
SYSCLK(4, pll2_sysclk4, pll2_pllen, 5, SYSCLK_ALWAYS_ENABLED);
SYSCLK(5, pll2_sysclk5, pll2_pllen, 5, SYSCLK_ALWAYS_ENABLED);
static const struct davinci_pll_obsclk_info dm365_pll2_obsclk_info = {
.name = "pll2_obsclk",
.parent_names = dm365_pll_obsclk_parent_names,
.num_parents = ARRAY_SIZE(dm365_pll_obsclk_parent_names),
.table = dm365_pll_obsclk_table,
.ocsrc_mask = BIT(4),
};
int dm365_pll2_init(struct device *dev, void __iomem *base)
{
struct clk *clk;
davinci_pll_clk_register(dev, &dm365_pll2_info, "oscin", base);
davinci_pll_sysclk_register(dev, &pll2_sysclk1, base);
clk = davinci_pll_sysclk_register(dev, &pll2_sysclk2, base);
clk_register_clkdev(clk, "pll1_sysclk2", "dm365-psc");
davinci_pll_sysclk_register(dev, &pll2_sysclk3, base);
clk = davinci_pll_sysclk_register(dev, &pll2_sysclk4, base);
clk_register_clkdev(clk, "pll1_sysclk4", "dm365-psc");
davinci_pll_sysclk_register(dev, &pll2_sysclk5, base);
davinci_pll_auxclk_register(dev, "pll2_auxclk", base);
davinci_pll_obsclk_register(dev, &dm365_pll2_obsclk_info, base);
return 0;
}

View File

@ -0,0 +1,80 @@
// SPDX-License-Identifier: GPL-2.0
/*
* PLL clock descriptions for TI DM644X
*
* Copyright (C) 2018 David Lechner <david@lechnology.com>
*/
#include <linux/bitops.h>
#include <linux/clkdev.h>
#include <linux/init.h>
#include <linux/types.h>
#include "pll.h"
static const struct davinci_pll_clk_info dm644x_pll1_info = {
.name = "pll1",
.pllm_mask = GENMASK(4, 0),
.pllm_min = 1,
.pllm_max = 32,
.pllout_min_rate = 400000000,
.pllout_max_rate = 600000000, /* 810MHz @ 1.3V, -810 only */
.flags = PLL_HAS_CLKMODE | PLL_HAS_POSTDIV,
};
SYSCLK(1, pll1_sysclk1, pll1_pllen, 4, SYSCLK_FIXED_DIV);
SYSCLK(2, pll1_sysclk2, pll1_pllen, 4, SYSCLK_FIXED_DIV);
SYSCLK(3, pll1_sysclk3, pll1_pllen, 4, SYSCLK_FIXED_DIV);
SYSCLK(5, pll1_sysclk5, pll1_pllen, 4, SYSCLK_FIXED_DIV);
int dm644x_pll1_init(struct device *dev, void __iomem *base)
{
struct clk *clk;
davinci_pll_clk_register(dev, &dm644x_pll1_info, "ref_clk", base);
clk = davinci_pll_sysclk_register(dev, &pll1_sysclk1, base);
clk_register_clkdev(clk, "pll1_sysclk1", "dm644x-psc");
clk = davinci_pll_sysclk_register(dev, &pll1_sysclk2, base);
clk_register_clkdev(clk, "pll1_sysclk2", "dm644x-psc");
clk = davinci_pll_sysclk_register(dev, &pll1_sysclk3, base);
clk_register_clkdev(clk, "pll1_sysclk3", "dm644x-psc");
clk = davinci_pll_sysclk_register(dev, &pll1_sysclk5, base);
clk_register_clkdev(clk, "pll1_sysclk5", "dm644x-psc");
clk = davinci_pll_auxclk_register(dev, "pll1_auxclk", base);
clk_register_clkdev(clk, "pll1_auxclk", "dm644x-psc");
davinci_pll_sysclkbp_clk_register(dev, "pll1_sysclkbp", base);
return 0;
}
static const struct davinci_pll_clk_info dm644x_pll2_info = {
.name = "pll2",
.pllm_mask = GENMASK(4, 0),
.pllm_min = 1,
.pllm_max = 32,
.pllout_min_rate = 400000000,
.pllout_max_rate = 900000000,
.flags = PLL_HAS_POSTDIV | PLL_POSTDIV_FIXED_DIV,
};
SYSCLK(1, pll2_sysclk1, pll2_pllen, 4, 0);
SYSCLK(2, pll2_sysclk2, pll2_pllen, 4, 0);
int dm644x_pll2_init(struct device *dev, void __iomem *base)
{
davinci_pll_clk_register(dev, &dm644x_pll2_info, "oscin", base);
davinci_pll_sysclk_register(dev, &pll2_sysclk1, base);
davinci_pll_sysclk_register(dev, &pll2_sysclk2, base);
davinci_pll_sysclkbp_clk_register(dev, "pll2_sysclkbp", base);
return 0;
}

View File

@ -0,0 +1,84 @@
// SPDX-License-Identifier: GPL-2.0
/*
* PLL clock descriptions for TI DM646X
*
* Copyright (C) 2018 David Lechner <david@lechnology.com>
*/
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/init.h>
#include <linux/types.h>
#include "pll.h"
static const struct davinci_pll_clk_info dm646x_pll1_info = {
.name = "pll1",
.pllm_mask = GENMASK(4, 0),
.pllm_min = 14,
.pllm_max = 32,
.flags = PLL_HAS_CLKMODE,
};
SYSCLK(1, pll1_sysclk1, pll1_pllen, 4, SYSCLK_FIXED_DIV);
SYSCLK(2, pll1_sysclk2, pll1_pllen, 4, SYSCLK_FIXED_DIV);
SYSCLK(3, pll1_sysclk3, pll1_pllen, 4, SYSCLK_FIXED_DIV);
SYSCLK(4, pll1_sysclk4, pll1_pllen, 4, 0);
SYSCLK(5, pll1_sysclk5, pll1_pllen, 4, 0);
SYSCLK(6, pll1_sysclk6, pll1_pllen, 4, 0);
SYSCLK(8, pll1_sysclk8, pll1_pllen, 4, 0);
SYSCLK(9, pll1_sysclk9, pll1_pllen, 4, 0);
int dm646x_pll1_init(struct device *dev, void __iomem *base)
{
struct clk *clk;
davinci_pll_clk_register(dev, &dm646x_pll1_info, "ref_clk", base);
clk = davinci_pll_sysclk_register(dev, &pll1_sysclk1, base);
clk_register_clkdev(clk, "pll1_sysclk1", "dm646x-psc");
clk = davinci_pll_sysclk_register(dev, &pll1_sysclk2, base);
clk_register_clkdev(clk, "pll1_sysclk2", "dm646x-psc");
clk = davinci_pll_sysclk_register(dev, &pll1_sysclk3, base);
clk_register_clkdev(clk, "pll1_sysclk3", "dm646x-psc");
clk_register_clkdev(clk, NULL, "davinci-wdt");
clk = davinci_pll_sysclk_register(dev, &pll1_sysclk4, base);
clk_register_clkdev(clk, "pll1_sysclk4", "dm646x-psc");
clk = davinci_pll_sysclk_register(dev, &pll1_sysclk5, base);
clk_register_clkdev(clk, "pll1_sysclk5", "dm646x-psc");
davinci_pll_sysclk_register(dev, &pll1_sysclk6, base);
davinci_pll_sysclk_register(dev, &pll1_sysclk8, base);
davinci_pll_sysclk_register(dev, &pll1_sysclk9, base);
davinci_pll_sysclkbp_clk_register(dev, "pll1_sysclkbp", base);
davinci_pll_auxclk_register(dev, "pll1_auxclk", base);
return 0;
}
static const struct davinci_pll_clk_info dm646x_pll2_info = {
.name = "pll2",
.pllm_mask = GENMASK(4, 0),
.pllm_min = 14,
.pllm_max = 32,
.flags = 0,
};
SYSCLK(1, pll2_sysclk1, pll2_pllen, 4, 0);
int dm646x_pll2_init(struct device *dev, void __iomem *base)
{
davinci_pll_clk_register(dev, &dm646x_pll2_info, "oscin", base);
davinci_pll_sysclk_register(dev, &pll2_sysclk1, base);
return 0;
}

899
drivers/clk/davinci/pll.c Normal file
View File

@ -0,0 +1,899 @@
// SPDX-License-Identifier: GPL-2.0
/*
* PLL clock driver for TI Davinci SoCs
*
* Copyright (C) 2018 David Lechner <david@lechnology.com>
*
* Based on arch/arm/mach-davinci/clock.c
* Copyright (C) 2006-2007 Texas Instruments.
* Copyright (C) 2008-2009 Deep Root Systems, LLC
*/
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/notifier.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/platform_data/clk-davinci-pll.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/types.h>
#include "pll.h"
#define MAX_NAME_SIZE 20
#define OSCIN_CLK_NAME "oscin"
#define REVID 0x000
#define PLLCTL 0x100
#define OCSEL 0x104
#define PLLSECCTL 0x108
#define PLLM 0x110
#define PREDIV 0x114
#define PLLDIV1 0x118
#define PLLDIV2 0x11c
#define PLLDIV3 0x120
#define OSCDIV 0x124
#define POSTDIV 0x128
#define BPDIV 0x12c
#define PLLCMD 0x138
#define PLLSTAT 0x13c
#define ALNCTL 0x140
#define DCHANGE 0x144
#define CKEN 0x148
#define CKSTAT 0x14c
#define SYSTAT 0x150
#define PLLDIV4 0x160
#define PLLDIV5 0x164
#define PLLDIV6 0x168
#define PLLDIV7 0x16c
#define PLLDIV8 0x170
#define PLLDIV9 0x174
#define PLLCTL_PLLEN BIT(0)
#define PLLCTL_PLLPWRDN BIT(1)
#define PLLCTL_PLLRST BIT(3)
#define PLLCTL_PLLDIS BIT(4)
#define PLLCTL_PLLENSRC BIT(5)
#define PLLCTL_CLKMODE BIT(8)
/* shared by most *DIV registers */
#define DIV_RATIO_SHIFT 0
#define DIV_RATIO_WIDTH 5
#define DIV_ENABLE_SHIFT 15
#define PLLCMD_GOSET BIT(0)
#define PLLSTAT_GOSTAT BIT(0)
#define CKEN_OBSCLK_SHIFT 1
#define CKEN_AUXEN_SHIFT 0
/*
* OMAP-L138 system reference guide recommends a wait for 4 OSCIN/CLKIN
* cycles to ensure that the PLLC has switched to bypass mode. Delay of 1us
* ensures we are good for all > 4MHz OSCIN/CLKIN inputs. Typically the input
* is ~25MHz. Units are micro seconds.
*/
#define PLL_BYPASS_TIME 1
/* From OMAP-L138 datasheet table 6-4. Units are micro seconds */
#define PLL_RESET_TIME 1
/*
* From OMAP-L138 datasheet table 6-4; assuming prediv = 1, sqrt(pllm) = 4
* Units are micro seconds.
*/
#define PLL_LOCK_TIME 20
/**
* struct davinci_pll_clk - Main PLL clock (aka PLLOUT)
* @hw: clk_hw for the pll
* @base: Base memory address
* @pllm_min: The minimum allowable PLLM[PLLM] value
* @pllm_max: The maxiumum allowable PLLM[PLLM] value
* @pllm_mask: Bitmask for PLLM[PLLM] value
*/
struct davinci_pll_clk {
struct clk_hw hw;
void __iomem *base;
u32 pllm_min;
u32 pllm_max;
u32 pllm_mask;
};
#define to_davinci_pll_clk(_hw) \
container_of((_hw), struct davinci_pll_clk, hw)
static unsigned long davinci_pll_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct davinci_pll_clk *pll = to_davinci_pll_clk(hw);
unsigned long rate = parent_rate;
u32 mult;
mult = readl(pll->base + PLLM) & pll->pllm_mask;
rate *= mult + 1;
return rate;
}
static int davinci_pll_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct davinci_pll_clk *pll = to_davinci_pll_clk(hw);
struct clk_hw *parent = req->best_parent_hw;
unsigned long parent_rate = req->best_parent_rate;
unsigned long rate = req->rate;
unsigned long best_rate, r;
u32 mult;
/* there is a limited range of valid outputs (see datasheet) */
if (rate < req->min_rate)
return -EINVAL;
rate = min(rate, req->max_rate);
mult = rate / parent_rate;
best_rate = parent_rate * mult;
/* easy case when there is no PREDIV */
if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) {
if (best_rate < req->min_rate)
return -EINVAL;
if (mult < pll->pllm_min || mult > pll->pllm_max)
return -EINVAL;
req->rate = best_rate;
return 0;
}
/* see if the PREDIV clock can help us */
best_rate = 0;
for (mult = pll->pllm_min; mult <= pll->pllm_max; mult++) {
parent_rate = clk_hw_round_rate(parent, rate / mult);
r = parent_rate * mult;
if (r < req->min_rate)
continue;
if (r > rate || r > req->max_rate)
break;
if (r > best_rate) {
best_rate = r;
req->rate = best_rate;
req->best_parent_rate = parent_rate;
if (best_rate == rate)
break;
}
}
return 0;
}
static int davinci_pll_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct davinci_pll_clk *pll = to_davinci_pll_clk(hw);
u32 mult;
mult = rate / parent_rate;
writel(mult - 1, pll->base + PLLM);
return 0;
}
#ifdef CONFIG_DEBUG_FS
static int davinci_pll_debug_init(struct clk_hw *hw, struct dentry *dentry);
#else
#define davinci_pll_debug_init NULL
#endif
static const struct clk_ops davinci_pll_ops = {
.recalc_rate = davinci_pll_recalc_rate,
.determine_rate = davinci_pll_determine_rate,
.set_rate = davinci_pll_set_rate,
.debug_init = davinci_pll_debug_init,
};
/* PLLM works differently on DM365 */
static unsigned long dm365_pll_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct davinci_pll_clk *pll = to_davinci_pll_clk(hw);
unsigned long rate = parent_rate;
u32 mult;
mult = readl(pll->base + PLLM) & pll->pllm_mask;
rate *= mult * 2;
return rate;
}
static const struct clk_ops dm365_pll_ops = {
.recalc_rate = dm365_pll_recalc_rate,
.debug_init = davinci_pll_debug_init,
};
/**
* davinci_pll_div_register - common *DIV clock implementation
* @name: the clock name
* @parent_name: the parent clock name
* @reg: the *DIV register
* @fixed: if true, the divider is a fixed value
* @flags: bitmap of CLK_* flags from clock-provider.h
*/
static struct clk *davinci_pll_div_register(struct device *dev,
const char *name,
const char *parent_name,
void __iomem *reg,
bool fixed, u32 flags)
{
const char * const *parent_names = parent_name ? &parent_name : NULL;
int num_parents = parent_name ? 1 : 0;
const struct clk_ops *divider_ops = &clk_divider_ops;
struct clk_gate *gate;
struct clk_divider *divider;
gate = devm_kzalloc(dev, sizeof(*gate), GFP_KERNEL);
if (!gate)
return ERR_PTR(-ENOMEM);
gate->reg = reg;
gate->bit_idx = DIV_ENABLE_SHIFT;
divider = devm_kzalloc(dev, sizeof(*divider), GFP_KERNEL);
if (!divider)
return ERR_PTR(-ENOMEM);
divider->reg = reg;
divider->shift = DIV_RATIO_SHIFT;
divider->width = DIV_RATIO_WIDTH;
if (fixed) {
divider->flags |= CLK_DIVIDER_READ_ONLY;
divider_ops = &clk_divider_ro_ops;
}
return clk_register_composite(dev, name, parent_names, num_parents,
NULL, NULL, &divider->hw, divider_ops,
&gate->hw, &clk_gate_ops, flags);
}
struct davinci_pllen_clk {
struct clk_hw hw;
void __iomem *base;
};
#define to_davinci_pllen_clk(_hw) \
container_of((_hw), struct davinci_pllen_clk, hw)
static const struct clk_ops davinci_pllen_ops = {
/* this clocks just uses the clock notification feature */
};
/*
* The PLL has to be switched into bypass mode while we are chaning the rate,
* so we do that on the PLLEN clock since it is the end of the line. This will
* switch to bypass before any of the parent clocks (PREDIV, PLL, POSTDIV) are
* changed and will switch back to the PLL after the changes have been made.
*/
static int davinci_pllen_rate_change(struct notifier_block *nb,
unsigned long flags, void *data)
{
struct clk_notifier_data *cnd = data;
struct clk_hw *hw = __clk_get_hw(cnd->clk);
struct davinci_pllen_clk *pll = to_davinci_pllen_clk(hw);
u32 ctrl;
ctrl = readl(pll->base + PLLCTL);
if (flags == PRE_RATE_CHANGE) {
/* Switch the PLL to bypass mode */
ctrl &= ~(PLLCTL_PLLENSRC | PLLCTL_PLLEN);
writel(ctrl, pll->base + PLLCTL);
udelay(PLL_BYPASS_TIME);
/* Reset and enable PLL */
ctrl &= ~(PLLCTL_PLLRST | PLLCTL_PLLDIS);
writel(ctrl, pll->base + PLLCTL);
} else {
udelay(PLL_RESET_TIME);
/* Bring PLL out of reset */
ctrl |= PLLCTL_PLLRST;
writel(ctrl, pll->base + PLLCTL);
udelay(PLL_LOCK_TIME);
/* Remove PLL from bypass mode */
ctrl |= PLLCTL_PLLEN;
writel(ctrl, pll->base + PLLCTL);
}
return NOTIFY_OK;
}
static struct davinci_pll_platform_data *davinci_pll_get_pdata(struct device *dev)
{
struct davinci_pll_platform_data *pdata = dev_get_platdata(dev);
/*
* Platform data is optional, so allocate a new struct if one was not
* provided. For device tree, this will always be the case.
*/
if (!pdata)
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return NULL;
/* for device tree, we need to fill in the struct */
if (dev->of_node)
pdata->cfgchip =
syscon_regmap_lookup_by_compatible("ti,da830-cfgchip");
return pdata;
}
static struct notifier_block davinci_pllen_notifier = {
.notifier_call = davinci_pllen_rate_change,
};
/**
* davinci_pll_clk_register - Register a PLL clock
* @info: The device-specific clock info
* @parent_name: The parent clock name
* @base: The PLL's memory region
*
* This creates a series of clocks that represent the PLL.
*
* OSCIN > [PREDIV >] PLL > [POSTDIV >] PLLEN
*
* - OSCIN is the parent clock (on secondary PLL, may come from primary PLL)
* - PREDIV and POSTDIV are optional (depends on the PLL controller)
* - PLL is the PLL output (aka PLLOUT)
* - PLLEN is the bypass multiplexer
*
* Returns: The PLLOUT clock or a negative error code.
*/
struct clk *davinci_pll_clk_register(struct device *dev,
const struct davinci_pll_clk_info *info,
const char *parent_name,
void __iomem *base)
{
struct davinci_pll_platform_data *pdata;
char prediv_name[MAX_NAME_SIZE];
char pllout_name[MAX_NAME_SIZE];
char postdiv_name[MAX_NAME_SIZE];
char pllen_name[MAX_NAME_SIZE];
struct clk_init_data init;
struct davinci_pll_clk *pllout;
struct davinci_pllen_clk *pllen;
struct clk *pllout_clk, *clk;
pdata = davinci_pll_get_pdata(dev);
if (!pdata)
return ERR_PTR(-ENOMEM);
if (info->flags & PLL_HAS_CLKMODE) {
/*
* If a PLL has PLLCTL[CLKMODE], then it is the primary PLL.
* We register a clock named "oscin" that serves as the internal
* "input clock" domain shared by both PLLs (if there are 2)
* and will be the parent clock to the AUXCLK, SYSCLKBP and
* OBSCLK domains. NB: The various TRMs use "OSCIN" to mean
* a number of different things. In this driver we use it to
* mean the signal after the PLLCTL[CLKMODE] switch.
*/
clk = clk_register_fixed_factor(dev, OSCIN_CLK_NAME,
parent_name, 0, 1, 1);
if (IS_ERR(clk))
return clk;
parent_name = OSCIN_CLK_NAME;
}
if (info->flags & PLL_HAS_PREDIV) {
bool fixed = info->flags & PLL_PREDIV_FIXED_DIV;
u32 flags = 0;
snprintf(prediv_name, MAX_NAME_SIZE, "%s_prediv", info->name);
if (info->flags & PLL_PREDIV_ALWAYS_ENABLED)
flags |= CLK_IS_CRITICAL;
/* Some? DM355 chips don't correctly report the PREDIV value */
if (info->flags & PLL_PREDIV_FIXED8)
clk = clk_register_fixed_factor(dev, prediv_name,
parent_name, flags, 1, 8);
else
clk = davinci_pll_div_register(dev, prediv_name,
parent_name, base + PREDIV, fixed, flags);
if (IS_ERR(clk))
return clk;
parent_name = prediv_name;
}
/* Unlock writing to PLL registers */
if (info->unlock_reg) {
if (IS_ERR_OR_NULL(pdata->cfgchip))
dev_warn(dev, "Failed to get CFGCHIP (%ld)\n",
PTR_ERR(pdata->cfgchip));
else
regmap_write_bits(pdata->cfgchip, info->unlock_reg,
info->unlock_mask, 0);
}
pllout = devm_kzalloc(dev, sizeof(*pllout), GFP_KERNEL);
if (!pllout)
return ERR_PTR(-ENOMEM);
snprintf(pllout_name, MAX_NAME_SIZE, "%s_pllout", info->name);
init.name = pllout_name;
if (info->flags & PLL_PLLM_2X)
init.ops = &dm365_pll_ops;
else
init.ops = &davinci_pll_ops;
init.parent_names = &parent_name;
init.num_parents = 1;
init.flags = 0;
if (info->flags & PLL_HAS_PREDIV)
init.flags |= CLK_SET_RATE_PARENT;
pllout->hw.init = &init;
pllout->base = base;
pllout->pllm_mask = info->pllm_mask;
pllout->pllm_min = info->pllm_min;
pllout->pllm_max = info->pllm_max;
pllout_clk = devm_clk_register(dev, &pllout->hw);
if (IS_ERR(pllout_clk))
return pllout_clk;
clk_hw_set_rate_range(&pllout->hw, info->pllout_min_rate,
info->pllout_max_rate);
parent_name = pllout_name;
if (info->flags & PLL_HAS_POSTDIV) {
bool fixed = info->flags & PLL_POSTDIV_FIXED_DIV;
u32 flags = CLK_SET_RATE_PARENT;
snprintf(postdiv_name, MAX_NAME_SIZE, "%s_postdiv", info->name);
if (info->flags & PLL_POSTDIV_ALWAYS_ENABLED)
flags |= CLK_IS_CRITICAL;
clk = davinci_pll_div_register(dev, postdiv_name, parent_name,
base + POSTDIV, fixed, flags);
if (IS_ERR(clk))
return clk;
parent_name = postdiv_name;
}
pllen = devm_kzalloc(dev, sizeof(*pllout), GFP_KERNEL);
if (!pllen)
return ERR_PTR(-ENOMEM);
snprintf(pllen_name, MAX_NAME_SIZE, "%s_pllen", info->name);
init.name = pllen_name;
init.ops = &davinci_pllen_ops;
init.parent_names = &parent_name;
init.num_parents = 1;
init.flags = CLK_SET_RATE_PARENT;
pllen->hw.init = &init;
pllen->base = base;
clk = devm_clk_register(dev, &pllen->hw);
if (IS_ERR(clk))
return clk;
clk_notifier_register(clk, &davinci_pllen_notifier);
return pllout_clk;
}
/**
* davinci_pll_auxclk_register - Register bypass clock (AUXCLK)
* @name: The clock name
* @base: The PLL memory region
*/
struct clk *davinci_pll_auxclk_register(struct device *dev,
const char *name,
void __iomem *base)
{
return clk_register_gate(dev, name, OSCIN_CLK_NAME, 0, base + CKEN,
CKEN_AUXEN_SHIFT, 0, NULL);
}
/**
* davinci_pll_sysclkbp_clk_register - Register bypass divider clock (SYSCLKBP)
* @name: The clock name
* @base: The PLL memory region
*/
struct clk *davinci_pll_sysclkbp_clk_register(struct device *dev,
const char *name,
void __iomem *base)
{
return clk_register_divider(dev, name, OSCIN_CLK_NAME, 0, base + BPDIV,
DIV_RATIO_SHIFT, DIV_RATIO_WIDTH,
CLK_DIVIDER_READ_ONLY, NULL);
}
/**
* davinci_pll_obsclk_register - Register oscillator divider clock (OBSCLK)
* @info: The clock info
* @base: The PLL memory region
*/
struct clk *
davinci_pll_obsclk_register(struct device *dev,
const struct davinci_pll_obsclk_info *info,
void __iomem *base)
{
struct clk_mux *mux;
struct clk_gate *gate;
struct clk_divider *divider;
u32 oscdiv;
mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
if (!mux)
return ERR_PTR(-ENOMEM);
mux->reg = base + OCSEL;
mux->table = info->table;
mux->mask = info->ocsrc_mask;
gate = devm_kzalloc(dev, sizeof(*gate), GFP_KERNEL);
if (!gate)
return ERR_PTR(-ENOMEM);
gate->reg = base + CKEN;
gate->bit_idx = CKEN_OBSCLK_SHIFT;
divider = devm_kzalloc(dev, sizeof(*divider), GFP_KERNEL);
if (!divider)
return ERR_PTR(-ENOMEM);
divider->reg = base + OSCDIV;
divider->shift = DIV_RATIO_SHIFT;
divider->width = DIV_RATIO_WIDTH;
/* make sure divider is enabled just in case bootloader disabled it */
oscdiv = readl(base + OSCDIV);
oscdiv |= BIT(DIV_ENABLE_SHIFT);
writel(oscdiv, base + OSCDIV);
return clk_register_composite(dev, info->name, info->parent_names,
info->num_parents,
&mux->hw, &clk_mux_ops,
&divider->hw, &clk_divider_ops,
&gate->hw, &clk_gate_ops, 0);
}
/* The PLL SYSCLKn clocks have a mechanism for synchronizing rate changes. */
static int davinci_pll_sysclk_rate_change(struct notifier_block *nb,
unsigned long flags, void *data)
{
struct clk_notifier_data *cnd = data;
struct clk_hw *hw = __clk_get_hw(clk_get_parent(cnd->clk));
struct davinci_pllen_clk *pll = to_davinci_pllen_clk(hw);
u32 pllcmd, pllstat;
switch (flags) {
case POST_RATE_CHANGE:
/* apply the changes */
pllcmd = readl(pll->base + PLLCMD);
pllcmd |= PLLCMD_GOSET;
writel(pllcmd, pll->base + PLLCMD);
/* fallthrough */
case PRE_RATE_CHANGE:
/* Wait until for outstanding changes to take effect */
do {
pllstat = readl(pll->base + PLLSTAT);
} while (pllstat & PLLSTAT_GOSTAT);
break;
}
return NOTIFY_OK;
}
static struct notifier_block davinci_pll_sysclk_notifier = {
.notifier_call = davinci_pll_sysclk_rate_change,
};
/**
* davinci_pll_sysclk_register - Register divider clocks (SYSCLKn)
* @info: The clock info
* @base: The PLL memory region
*/
struct clk *
davinci_pll_sysclk_register(struct device *dev,
const struct davinci_pll_sysclk_info *info,
void __iomem *base)
{
const struct clk_ops *divider_ops = &clk_divider_ops;
struct clk_gate *gate;
struct clk_divider *divider;
struct clk *clk;
u32 reg;
u32 flags = 0;
/* PLLDIVn registers are not entirely consecutive */
if (info->id < 4)
reg = PLLDIV1 + 4 * (info->id - 1);
else
reg = PLLDIV4 + 4 * (info->id - 4);
gate = devm_kzalloc(dev, sizeof(*gate), GFP_KERNEL);
if (!gate)
return ERR_PTR(-ENOMEM);
gate->reg = base + reg;
gate->bit_idx = DIV_ENABLE_SHIFT;
divider = devm_kzalloc(dev, sizeof(*divider), GFP_KERNEL);
if (!divider)
return ERR_PTR(-ENOMEM);
divider->reg = base + reg;
divider->shift = DIV_RATIO_SHIFT;
divider->width = info->ratio_width;
divider->flags = 0;
if (info->flags & SYSCLK_FIXED_DIV) {
divider->flags |= CLK_DIVIDER_READ_ONLY;
divider_ops = &clk_divider_ro_ops;
}
/* Only the ARM clock can change the parent PLL rate */
if (info->flags & SYSCLK_ARM_RATE)
flags |= CLK_SET_RATE_PARENT;
if (info->flags & SYSCLK_ALWAYS_ENABLED)
flags |= CLK_IS_CRITICAL;
clk = clk_register_composite(dev, info->name, &info->parent_name, 1,
NULL, NULL, &divider->hw, divider_ops,
&gate->hw, &clk_gate_ops, flags);
if (IS_ERR(clk))
return clk;
clk_notifier_register(clk, &davinci_pll_sysclk_notifier);
return clk;
}
int of_davinci_pll_init(struct device *dev,
const struct davinci_pll_clk_info *info,
const struct davinci_pll_obsclk_info *obsclk_info,
const struct davinci_pll_sysclk_info **div_info,
u8 max_sysclk_id,
void __iomem *base)
{
struct device_node *node = dev->of_node;
struct device_node *child;
const char *parent_name;
struct clk *clk;
if (info->flags & PLL_HAS_CLKMODE)
parent_name = of_clk_get_parent_name(node, 0);
else
parent_name = OSCIN_CLK_NAME;
clk = davinci_pll_clk_register(dev, info, parent_name, base);
if (IS_ERR(clk)) {
dev_err(dev, "failed to register %s\n", info->name);
return PTR_ERR(clk);
}
child = of_get_child_by_name(node, "pllout");
if (of_device_is_available(child))
of_clk_add_provider(child, of_clk_src_simple_get, clk);
of_node_put(child);
child = of_get_child_by_name(node, "sysclk");
if (of_device_is_available(child)) {
struct clk_onecell_data *clk_data;
struct clk **clks;
int n_clks = max_sysclk_id + 1;
int i;
clk_data = devm_kzalloc(dev, sizeof(*clk_data), GFP_KERNEL);
if (!clk_data)
return -ENOMEM;
clks = devm_kmalloc_array(dev, n_clks, sizeof(*clks), GFP_KERNEL);
if (!clks)
return -ENOMEM;
clk_data->clks = clks;
clk_data->clk_num = n_clks;
for (i = 0; i < n_clks; i++)
clks[i] = ERR_PTR(-ENOENT);
for (; *div_info; div_info++) {
clk = davinci_pll_sysclk_register(dev, *div_info, base);
if (IS_ERR(clk))
dev_warn(dev, "failed to register %s (%ld)\n",
(*div_info)->name, PTR_ERR(clk));
else
clks[(*div_info)->id] = clk;
}
of_clk_add_provider(child, of_clk_src_onecell_get, clk_data);
}
of_node_put(child);
child = of_get_child_by_name(node, "auxclk");
if (of_device_is_available(child)) {
char child_name[MAX_NAME_SIZE];
snprintf(child_name, MAX_NAME_SIZE, "%s_auxclk", info->name);
clk = davinci_pll_auxclk_register(dev, child_name, base);
if (IS_ERR(clk))
dev_warn(dev, "failed to register %s (%ld)\n",
child_name, PTR_ERR(clk));
else
of_clk_add_provider(child, of_clk_src_simple_get, clk);
}
of_node_put(child);
child = of_get_child_by_name(node, "obsclk");
if (of_device_is_available(child)) {
if (obsclk_info)
clk = davinci_pll_obsclk_register(dev, obsclk_info, base);
else
clk = ERR_PTR(-EINVAL);
if (IS_ERR(clk))
dev_warn(dev, "failed to register obsclk (%ld)\n",
PTR_ERR(clk));
else
of_clk_add_provider(child, of_clk_src_simple_get, clk);
}
of_node_put(child);
return 0;
}
static const struct of_device_id davinci_pll_of_match[] = {
{ .compatible = "ti,da850-pll0", .data = of_da850_pll0_init },
{ .compatible = "ti,da850-pll1", .data = of_da850_pll1_init },
{ }
};
static const struct platform_device_id davinci_pll_id_table[] = {
{ .name = "da830-pll", .driver_data = (kernel_ulong_t)da830_pll_init },
{ .name = "da850-pll0", .driver_data = (kernel_ulong_t)da850_pll0_init },
{ .name = "da850-pll1", .driver_data = (kernel_ulong_t)da850_pll1_init },
{ .name = "dm355-pll1", .driver_data = (kernel_ulong_t)dm355_pll1_init },
{ .name = "dm355-pll2", .driver_data = (kernel_ulong_t)dm355_pll2_init },
{ .name = "dm365-pll1", .driver_data = (kernel_ulong_t)dm365_pll1_init },
{ .name = "dm365-pll2", .driver_data = (kernel_ulong_t)dm365_pll2_init },
{ .name = "dm644x-pll1", .driver_data = (kernel_ulong_t)dm644x_pll1_init },
{ .name = "dm644x-pll2", .driver_data = (kernel_ulong_t)dm644x_pll2_init },
{ .name = "dm646x-pll1", .driver_data = (kernel_ulong_t)dm646x_pll1_init },
{ .name = "dm646x-pll2", .driver_data = (kernel_ulong_t)dm646x_pll2_init },
{ }
};
typedef int (*davinci_pll_init)(struct device *dev, void __iomem *base);
static int davinci_pll_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct of_device_id *of_id;
davinci_pll_init pll_init = NULL;
struct resource *res;
void __iomem *base;
of_id = of_match_device(davinci_pll_of_match, dev);
if (of_id)
pll_init = of_id->data;
else if (pdev->id_entry)
pll_init = (void *)pdev->id_entry->driver_data;
if (!pll_init) {
dev_err(dev, "unable to find driver data\n");
return -EINVAL;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
return pll_init(dev, base);
}
static struct platform_driver davinci_pll_driver = {
.probe = davinci_pll_probe,
.driver = {
.name = "davinci-pll-clk",
.of_match_table = davinci_pll_of_match,
},
.id_table = davinci_pll_id_table,
};
static int __init davinci_pll_driver_init(void)
{
return platform_driver_register(&davinci_pll_driver);
}
/* has to be postcore_initcall because PSC devices depend on PLL parent clocks */
postcore_initcall(davinci_pll_driver_init);
#ifdef CONFIG_DEBUG_FS
#include <linux/debugfs.h>
#define DEBUG_REG(n) \
{ \
.name = #n, \
.offset = n, \
}
static const struct debugfs_reg32 davinci_pll_regs[] = {
DEBUG_REG(REVID),
DEBUG_REG(PLLCTL),
DEBUG_REG(OCSEL),
DEBUG_REG(PLLSECCTL),
DEBUG_REG(PLLM),
DEBUG_REG(PREDIV),
DEBUG_REG(PLLDIV1),
DEBUG_REG(PLLDIV2),
DEBUG_REG(PLLDIV3),
DEBUG_REG(OSCDIV),
DEBUG_REG(POSTDIV),
DEBUG_REG(BPDIV),
DEBUG_REG(PLLCMD),
DEBUG_REG(PLLSTAT),
DEBUG_REG(ALNCTL),
DEBUG_REG(DCHANGE),
DEBUG_REG(CKEN),
DEBUG_REG(CKSTAT),
DEBUG_REG(SYSTAT),
DEBUG_REG(PLLDIV4),
DEBUG_REG(PLLDIV5),
DEBUG_REG(PLLDIV6),
DEBUG_REG(PLLDIV7),
DEBUG_REG(PLLDIV8),
DEBUG_REG(PLLDIV9),
};
static int davinci_pll_debug_init(struct clk_hw *hw, struct dentry *dentry)
{
struct davinci_pll_clk *pll = to_davinci_pll_clk(hw);
struct debugfs_regset32 *regset;
struct dentry *d;
regset = kzalloc(sizeof(*regset), GFP_KERNEL);
if (!regset)
return -ENOMEM;
regset->regs = davinci_pll_regs;
regset->nregs = ARRAY_SIZE(davinci_pll_regs);
regset->base = pll->base;
d = debugfs_create_regset32("registers", 0400, dentry, regset);
if (IS_ERR(d)) {
kfree(regset);
return PTR_ERR(d);
}
return 0;
}
#endif

141
drivers/clk/davinci/pll.h Normal file
View File

@ -0,0 +1,141 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Clock driver for TI Davinci PSC controllers
*
* Copyright (C) 2018 David Lechner <david@lechnology.com>
*/
#ifndef __CLK_DAVINCI_PLL_H___
#define __CLK_DAVINCI_PLL_H___
#include <linux/bitops.h>
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/types.h>
#define PLL_HAS_CLKMODE BIT(0) /* PLL has PLLCTL[CLKMODE] */
#define PLL_HAS_PREDIV BIT(1) /* has prediv before PLL */
#define PLL_PREDIV_ALWAYS_ENABLED BIT(2) /* don't clear DEN bit */
#define PLL_PREDIV_FIXED_DIV BIT(3) /* fixed divider value */
#define PLL_HAS_POSTDIV BIT(4) /* has postdiv after PLL */
#define PLL_POSTDIV_ALWAYS_ENABLED BIT(5) /* don't clear DEN bit */
#define PLL_POSTDIV_FIXED_DIV BIT(6) /* fixed divider value */
#define PLL_HAS_EXTCLKSRC BIT(7) /* has selectable bypass */
#define PLL_PLLM_2X BIT(8) /* PLLM value is 2x (DM365) */
#define PLL_PREDIV_FIXED8 BIT(9) /* DM355 quirk */
/** davinci_pll_clk_info - controller-specific PLL info
* @name: The name of the PLL
* @unlock_reg: Option CFGCHIP register for unlocking PLL
* @unlock_mask: Bitmask used with @unlock_reg
* @pllm_mask: Bitmask for PLLM[PLLM] value
* @pllm_min: Minimum allowable value for PLLM[PLLM]
* @pllm_max: Maximum allowable value for PLLM[PLLM]
* @pllout_min_rate: Minimum allowable rate for PLLOUT
* @pllout_max_rate: Maximum allowable rate for PLLOUT
* @flags: Bitmap of PLL_* flags.
*/
struct davinci_pll_clk_info {
const char *name;
u32 unlock_reg;
u32 unlock_mask;
u32 pllm_mask;
u32 pllm_min;
u32 pllm_max;
unsigned long pllout_min_rate;
unsigned long pllout_max_rate;
u32 flags;
};
#define SYSCLK_ARM_RATE BIT(0) /* Controls ARM rate */
#define SYSCLK_ALWAYS_ENABLED BIT(1) /* Or bad things happen */
#define SYSCLK_FIXED_DIV BIT(2) /* Fixed divider */
/** davinci_pll_sysclk_info - SYSCLKn-specific info
* @name: The name of the clock
* @parent_name: The name of the parent clock
* @id: "n" in "SYSCLKn"
* @ratio_width: Width (in bits) of RATIO in PLLDIVn register
* @flags: Bitmap of SYSCLK_* flags.
*/
struct davinci_pll_sysclk_info {
const char *name;
const char *parent_name;
u32 id;
u32 ratio_width;
u32 flags;
};
#define SYSCLK(i, n, p, w, f) \
static const struct davinci_pll_sysclk_info n = { \
.name = #n, \
.parent_name = #p, \
.id = (i), \
.ratio_width = (w), \
.flags = (f), \
}
/** davinci_pll_obsclk_info - OBSCLK-specific info
* @name: The name of the clock
* @parent_names: Array of names of the parent clocks
* @num_parents: Length of @parent_names
* @table: Array of values to write to OCSEL[OCSRC] cooresponding to
* @parent_names
* @ocsrc_mask: Bitmask for OCSEL[OCSRC]
*/
struct davinci_pll_obsclk_info {
const char *name;
const char * const *parent_names;
u8 num_parents;
u32 *table;
u32 ocsrc_mask;
};
struct clk *davinci_pll_clk_register(struct device *dev,
const struct davinci_pll_clk_info *info,
const char *parent_name,
void __iomem *base);
struct clk *davinci_pll_auxclk_register(struct device *dev,
const char *name,
void __iomem *base);
struct clk *davinci_pll_sysclkbp_clk_register(struct device *dev,
const char *name,
void __iomem *base);
struct clk *
davinci_pll_obsclk_register(struct device *dev,
const struct davinci_pll_obsclk_info *info,
void __iomem *base);
struct clk *
davinci_pll_sysclk_register(struct device *dev,
const struct davinci_pll_sysclk_info *info,
void __iomem *base);
int of_davinci_pll_init(struct device *dev,
const struct davinci_pll_clk_info *info,
const struct davinci_pll_obsclk_info *obsclk_info,
const struct davinci_pll_sysclk_info **div_info,
u8 max_sysclk_id,
void __iomem *base);
/* Platform-specific callbacks */
int da830_pll_init(struct device *dev, void __iomem *base);
int da850_pll0_init(struct device *dev, void __iomem *base);
int da850_pll1_init(struct device *dev, void __iomem *base);
int of_da850_pll0_init(struct device *dev, void __iomem *base);
int of_da850_pll1_init(struct device *dev, void __iomem *base);
int dm355_pll1_init(struct device *dev, void __iomem *base);
int dm355_pll2_init(struct device *dev, void __iomem *base);
int dm365_pll1_init(struct device *dev, void __iomem *base);
int dm365_pll2_init(struct device *dev, void __iomem *base);
int dm644x_pll1_init(struct device *dev, void __iomem *base);
int dm644x_pll2_init(struct device *dev, void __iomem *base);
int dm646x_pll1_init(struct device *dev, void __iomem *base);
int dm646x_pll2_init(struct device *dev, void __iomem *base);
#endif /* __CLK_DAVINCI_PLL_H___ */

View File

@ -0,0 +1,116 @@
// SPDX-License-Identifier: GPL-2.0
/*
* PSC clock descriptions for TI DA830/OMAP-L137/AM17XX
*
* Copyright (C) 2018 David Lechner <david@lechnology.com>
*/
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include "psc.h"
LPSC_CLKDEV1(spi0_clkdev, NULL, "spi_davinci.0");
LPSC_CLKDEV1(mmcsd_clkdev, NULL, "da830-mmc.0");
LPSC_CLKDEV1(uart0_clkdev, NULL, "serial8250.0");
static const struct davinci_lpsc_clk_info da830_psc0_info[] = {
LPSC(0, 0, tpcc, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
LPSC(1, 0, tptc0, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
LPSC(2, 0, tptc1, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
LPSC(3, 0, aemif, pll0_sysclk3, NULL, LPSC_ALWAYS_ENABLED),
LPSC(4, 0, spi0, pll0_sysclk2, spi0_clkdev, 0),
LPSC(5, 0, mmcsd, pll0_sysclk2, mmcsd_clkdev, 0),
LPSC(6, 0, aintc, pll0_sysclk4, NULL, LPSC_ALWAYS_ENABLED),
LPSC(7, 0, arm_rom, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
LPSC(8, 0, secu_mgr, pll0_sysclk4, NULL, LPSC_ALWAYS_ENABLED),
LPSC(9, 0, uart0, pll0_sysclk2, uart0_clkdev, 0),
LPSC(10, 0, scr0_ss, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
LPSC(11, 0, scr1_ss, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
LPSC(12, 0, scr2_ss, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
LPSC(13, 0, pruss, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
LPSC(14, 0, arm, pll0_sysclk6, NULL, LPSC_ALWAYS_ENABLED),
{ }
};
static int da830_psc0_init(struct device *dev, void __iomem *base)
{
return davinci_psc_register_clocks(dev, da830_psc0_info, 16, base);
}
static struct clk_bulk_data da830_psc0_parent_clks[] = {
{ .id = "pll0_sysclk2" },
{ .id = "pll0_sysclk3" },
{ .id = "pll0_sysclk4" },
{ .id = "pll0_sysclk6" },
};
const struct davinci_psc_init_data da830_psc0_init_data = {
.parent_clks = da830_psc0_parent_clks,
.num_parent_clks = ARRAY_SIZE(da830_psc0_parent_clks),
.psc_init = &da830_psc0_init,
};
LPSC_CLKDEV2(usb0_clkdev, NULL, "musb-da8xx",
NULL, "cppi41-dmaengine");
LPSC_CLKDEV1(usb1_clkdev, NULL, "ohci-da8xx");
/* REVISIT: gpio-davinci.c should be modified to drop con_id */
LPSC_CLKDEV1(gpio_clkdev, "gpio", NULL);
LPSC_CLKDEV2(emac_clkdev, NULL, "davinci_emac.1",
"fck", "davinci_mdio.0");
LPSC_CLKDEV1(mcasp0_clkdev, NULL, "davinci-mcasp.0");
LPSC_CLKDEV1(mcasp1_clkdev, NULL, "davinci-mcasp.1");
LPSC_CLKDEV1(mcasp2_clkdev, NULL, "davinci-mcasp.2");
LPSC_CLKDEV1(spi1_clkdev, NULL, "spi_davinci.1");
LPSC_CLKDEV1(i2c1_clkdev, NULL, "i2c_davinci.2");
LPSC_CLKDEV1(uart1_clkdev, NULL, "serial8250.1");
LPSC_CLKDEV1(uart2_clkdev, NULL, "serial8250.2");
LPSC_CLKDEV1(lcdc_clkdev, "fck", "da8xx_lcdc.0");
LPSC_CLKDEV2(pwm_clkdev, "fck", "ehrpwm.0",
"fck", "ehrpwm.1");
LPSC_CLKDEV3(ecap_clkdev, "fck", "ecap.0",
"fck", "ecap.1",
"fck", "ecap.2");
LPSC_CLKDEV2(eqep_clkdev, NULL, "eqep.0",
NULL, "eqep.1");
static const struct davinci_lpsc_clk_info da830_psc1_info[] = {
LPSC(1, 0, usb0, pll0_sysclk2, usb0_clkdev, 0),
LPSC(2, 0, usb1, pll0_sysclk4, usb1_clkdev, 0),
LPSC(3, 0, gpio, pll0_sysclk4, gpio_clkdev, 0),
LPSC(5, 0, emac, pll0_sysclk4, emac_clkdev, 0),
LPSC(6, 0, emif3, pll0_sysclk5, NULL, LPSC_ALWAYS_ENABLED),
LPSC(7, 0, mcasp0, pll0_sysclk2, mcasp0_clkdev, 0),
LPSC(8, 0, mcasp1, pll0_sysclk2, mcasp1_clkdev, 0),
LPSC(9, 0, mcasp2, pll0_sysclk2, mcasp2_clkdev, 0),
LPSC(10, 0, spi1, pll0_sysclk2, spi1_clkdev, 0),
LPSC(11, 0, i2c1, pll0_sysclk4, i2c1_clkdev, 0),
LPSC(12, 0, uart1, pll0_sysclk2, uart1_clkdev, 0),
LPSC(13, 0, uart2, pll0_sysclk2, uart2_clkdev, 0),
LPSC(16, 0, lcdc, pll0_sysclk2, lcdc_clkdev, 0),
LPSC(17, 0, pwm, pll0_sysclk2, pwm_clkdev, 0),
LPSC(20, 0, ecap, pll0_sysclk2, ecap_clkdev, 0),
LPSC(21, 0, eqep, pll0_sysclk2, eqep_clkdev, 0),
{ }
};
static int da830_psc1_init(struct device *dev, void __iomem *base)
{
return davinci_psc_register_clocks(dev, da830_psc1_info, 32, base);
}
static struct clk_bulk_data da830_psc1_parent_clks[] = {
{ .id = "pll0_sysclk2" },
{ .id = "pll0_sysclk4" },
{ .id = "pll0_sysclk5" },
};
const struct davinci_psc_init_data da830_psc1_init_data = {
.parent_clks = da830_psc1_parent_clks,
.num_parent_clks = ARRAY_SIZE(da830_psc1_parent_clks),
.psc_init = &da830_psc1_init,
};

View File

@ -0,0 +1,149 @@
// SPDX-License-Identifier: GPL-2.0
/*
* PSC clock descriptions for TI DA850/OMAP-L138/AM18XX
*
* Copyright (C) 2018 David Lechner <david@lechnology.com>
*/
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/types.h>
#include "psc.h"
LPSC_CLKDEV2(emifa_clkdev, NULL, "ti-aemif",
"aemif", "davinci_nand.0");
LPSC_CLKDEV1(spi0_clkdev, NULL, "spi_davinci.0");
LPSC_CLKDEV1(mmcsd0_clkdev, NULL, "da830-mmc.0");
LPSC_CLKDEV1(uart0_clkdev, NULL, "serial8250.0");
/* REVISIT: used dev_id instead of con_id */
LPSC_CLKDEV1(arm_clkdev, "arm", NULL);
LPSC_CLKDEV1(dsp_clkdev, NULL, "davinci-rproc.0");
static const struct davinci_lpsc_clk_info da850_psc0_info[] = {
LPSC(0, 0, tpcc0, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
LPSC(1, 0, tptc0, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
LPSC(2, 0, tptc1, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
LPSC(3, 0, emifa, async1, emifa_clkdev, 0),
LPSC(4, 0, spi0, pll0_sysclk2, spi0_clkdev, 0),
LPSC(5, 0, mmcsd0, pll0_sysclk2, mmcsd0_clkdev, 0),
LPSC(6, 0, aintc, pll0_sysclk4, NULL, LPSC_ALWAYS_ENABLED),
LPSC(7, 0, arm_rom, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
LPSC(9, 0, uart0, pll0_sysclk2, uart0_clkdev, 0),
LPSC(13, 0, pruss, pll0_sysclk2, NULL, 0),
LPSC(14, 0, arm, pll0_sysclk6, arm_clkdev, LPSC_ALWAYS_ENABLED | LPSC_SET_RATE_PARENT),
LPSC(15, 1, dsp, pll0_sysclk1, dsp_clkdev, LPSC_FORCE | LPSC_LOCAL_RESET),
{ }
};
LPSC_CLKDEV3(usb0_clkdev, "fck", "da830-usb-phy-clks",
NULL, "musb-da8xx",
NULL, "cppi41-dmaengine");
LPSC_CLKDEV1(usb1_clkdev, NULL, "ohci-da8xx");
/* REVISIT: gpio-davinci.c should be modified to drop con_id */
LPSC_CLKDEV1(gpio_clkdev, "gpio", NULL);
LPSC_CLKDEV2(emac_clkdev, NULL, "davinci_emac.1",
"fck", "davinci_mdio.0");
LPSC_CLKDEV1(mcasp0_clkdev, NULL, "davinci-mcasp.0");
LPSC_CLKDEV1(sata_clkdev, "fck", "ahci_da850");
LPSC_CLKDEV1(vpif_clkdev, NULL, "vpif");
LPSC_CLKDEV1(spi1_clkdev, NULL, "spi_davinci.1");
LPSC_CLKDEV1(i2c1_clkdev, NULL, "i2c_davinci.2");
LPSC_CLKDEV1(uart1_clkdev, NULL, "serial8250.1");
LPSC_CLKDEV1(uart2_clkdev, NULL, "serial8250.2");
LPSC_CLKDEV1(mcbsp0_clkdev, NULL, "davinci-mcbsp.0");
LPSC_CLKDEV1(mcbsp1_clkdev, NULL, "davinci-mcbsp.1");
LPSC_CLKDEV1(lcdc_clkdev, "fck", "da8xx_lcdc.0");
LPSC_CLKDEV3(ehrpwm_clkdev, "fck", "ehrpwm.0",
"fck", "ehrpwm.1",
NULL, "da830-tbclksync");
LPSC_CLKDEV1(mmcsd1_clkdev, NULL, "da830-mmc.1");
LPSC_CLKDEV3(ecap_clkdev, "fck", "ecap.0",
"fck", "ecap.1",
"fck", "ecap.2");
static int da850_psc0_init(struct device *dev, void __iomem *base)
{
return davinci_psc_register_clocks(dev, da850_psc0_info, 16, base);
}
static int of_da850_psc0_init(struct device *dev, void __iomem *base)
{
return of_davinci_psc_clk_init(dev, da850_psc0_info, 16, base);
}
static struct clk_bulk_data da850_psc0_parent_clks[] = {
{ .id = "pll0_sysclk1" },
{ .id = "pll0_sysclk2" },
{ .id = "pll0_sysclk4" },
{ .id = "pll0_sysclk6" },
{ .id = "async1" },
};
const struct davinci_psc_init_data da850_psc0_init_data = {
.parent_clks = da850_psc0_parent_clks,
.num_parent_clks = ARRAY_SIZE(da850_psc0_parent_clks),
.psc_init = &da850_psc0_init,
};
const struct davinci_psc_init_data of_da850_psc0_init_data = {
.parent_clks = da850_psc0_parent_clks,
.num_parent_clks = ARRAY_SIZE(da850_psc0_parent_clks),
.psc_init = &of_da850_psc0_init,
};
static const struct davinci_lpsc_clk_info da850_psc1_info[] = {
LPSC(0, 0, tpcc1, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
LPSC(1, 0, usb0, pll0_sysclk2, usb0_clkdev, 0),
LPSC(2, 0, usb1, pll0_sysclk4, usb1_clkdev, 0),
LPSC(3, 0, gpio, pll0_sysclk4, gpio_clkdev, 0),
LPSC(5, 0, emac, pll0_sysclk4, emac_clkdev, 0),
LPSC(6, 0, ddr, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
LPSC(7, 0, mcasp0, async3, mcasp0_clkdev, 0),
LPSC(8, 0, sata, pll0_sysclk2, sata_clkdev, LPSC_FORCE),
LPSC(9, 0, vpif, pll0_sysclk2, vpif_clkdev, 0),
LPSC(10, 0, spi1, async3, spi1_clkdev, 0),
LPSC(11, 0, i2c1, pll0_sysclk4, i2c1_clkdev, 0),
LPSC(12, 0, uart1, async3, uart1_clkdev, 0),
LPSC(13, 0, uart2, async3, uart2_clkdev, 0),
LPSC(14, 0, mcbsp0, async3, mcbsp0_clkdev, 0),
LPSC(15, 0, mcbsp1, async3, mcbsp1_clkdev, 0),
LPSC(16, 0, lcdc, pll0_sysclk2, lcdc_clkdev, 0),
LPSC(17, 0, ehrpwm, async3, ehrpwm_clkdev, 0),
LPSC(18, 0, mmcsd1, pll0_sysclk2, mmcsd1_clkdev, 0),
LPSC(20, 0, ecap, async3, ecap_clkdev, 0),
LPSC(21, 0, tptc2, pll0_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
{ }
};
static int da850_psc1_init(struct device *dev, void __iomem *base)
{
return davinci_psc_register_clocks(dev, da850_psc1_info, 32, base);
}
static int of_da850_psc1_init(struct device *dev, void __iomem *base)
{
return of_davinci_psc_clk_init(dev, da850_psc1_info, 32, base);
}
static struct clk_bulk_data da850_psc1_parent_clks[] = {
{ .id = "pll0_sysclk2" },
{ .id = "pll0_sysclk4" },
{ .id = "async3" },
};
const struct davinci_psc_init_data da850_psc1_init_data = {
.parent_clks = da850_psc1_parent_clks,
.num_parent_clks = ARRAY_SIZE(da850_psc1_parent_clks),
.psc_init = &da850_psc1_init,
};
const struct davinci_psc_init_data of_da850_psc1_init_data = {
.parent_clks = da850_psc1_parent_clks,
.num_parent_clks = ARRAY_SIZE(da850_psc1_parent_clks),
.psc_init = &of_da850_psc1_init,
};

View File

@ -0,0 +1,88 @@
// SPDX-License-Identifier: GPL-2.0
/*
* PSC clock descriptions for TI DaVinci DM355
*
* Copyright (C) 2018 David Lechner <david@lechnology.com>
*/
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include "psc.h"
LPSC_CLKDEV1(vpss_master_clkdev, "master", "vpss");
LPSC_CLKDEV1(vpss_slave_clkdev, "slave", "vpss");
LPSC_CLKDEV1(spi1_clkdev, NULL, "spi_davinci.1");
LPSC_CLKDEV1(mmcsd1_clkdev, NULL, "dm6441-mmc.1");
LPSC_CLKDEV1(mcbsp1_clkdev, NULL, "davinci-mcbsp.1");
LPSC_CLKDEV1(usb_clkdev, "usb", NULL);
LPSC_CLKDEV1(spi2_clkdev, NULL, "spi_davinci.2");
LPSC_CLKDEV1(aemif_clkdev, "aemif", NULL);
LPSC_CLKDEV1(mmcsd0_clkdev, NULL, "dm6441-mmc.0");
LPSC_CLKDEV1(mcbsp0_clkdev, NULL, "davinci-mcbsp.0");
LPSC_CLKDEV1(i2c_clkdev, NULL, "i2c_davinci.1");
LPSC_CLKDEV1(uart0_clkdev, NULL, "serial8250.0");
LPSC_CLKDEV1(uart1_clkdev, NULL, "serial8250.1");
LPSC_CLKDEV1(uart2_clkdev, NULL, "serial8250.2");
LPSC_CLKDEV1(spi0_clkdev, NULL, "spi_davinci.0");
/* REVISIT: gpio-davinci.c should be modified to drop con_id */
LPSC_CLKDEV1(gpio_clkdev, "gpio", NULL);
LPSC_CLKDEV1(timer0_clkdev, "timer0", NULL);
LPSC_CLKDEV1(timer2_clkdev, NULL, "davinci-wdt");
LPSC_CLKDEV1(vpss_dac_clkdev, "vpss_dac", NULL);
static const struct davinci_lpsc_clk_info dm355_psc_info[] = {
LPSC(0, 0, vpss_master, pll1_sysclk4, vpss_master_clkdev, 0),
LPSC(1, 0, vpss_slave, pll1_sysclk4, vpss_slave_clkdev, 0),
LPSC(5, 0, timer3, pll1_auxclk, NULL, 0),
LPSC(6, 0, spi1, pll1_sysclk2, spi1_clkdev, 0),
LPSC(7, 0, mmcsd1, pll1_sysclk2, mmcsd1_clkdev, 0),
LPSC(8, 0, asp1, pll1_sysclk2, NULL, 0),
LPSC(9, 0, usb, pll1_sysclk2, usb_clkdev, 0),
LPSC(10, 0, pwm3, pll1_auxclk, NULL, 0),
LPSC(11, 0, spi2, pll1_sysclk2, spi2_clkdev, 0),
LPSC(12, 0, rto, pll1_auxclk, NULL, 0),
LPSC(14, 0, aemif, pll1_sysclk2, aemif_clkdev, 0),
LPSC(15, 0, mmcsd0, pll1_sysclk2, mmcsd0_clkdev, 0),
LPSC(17, 0, asp0, pll1_sysclk2, NULL, 0),
LPSC(18, 0, i2c, pll1_auxclk, i2c_clkdev, 0),
LPSC(19, 0, uart0, pll1_auxclk, uart0_clkdev, 0),
LPSC(20, 0, uart1, pll1_auxclk, uart1_clkdev, 0),
LPSC(21, 0, uart2, pll1_sysclk2, uart2_clkdev, 0),
LPSC(22, 0, spi0, pll1_sysclk2, spi0_clkdev, 0),
LPSC(23, 0, pwm0, pll1_auxclk, NULL, 0),
LPSC(24, 0, pwm1, pll1_auxclk, NULL, 0),
LPSC(25, 0, pwm2, pll1_auxclk, NULL, 0),
LPSC(26, 0, gpio, pll1_sysclk2, gpio_clkdev, 0),
LPSC(27, 0, timer0, pll1_auxclk, timer0_clkdev, LPSC_ALWAYS_ENABLED),
LPSC(28, 0, timer1, pll1_auxclk, NULL, 0),
/* REVISIT: why can't this be disabled? */
LPSC(29, 0, timer2, pll1_auxclk, timer2_clkdev, LPSC_ALWAYS_ENABLED),
LPSC(31, 0, arm, pll1_sysclk1, NULL, LPSC_ALWAYS_ENABLED),
LPSC(40, 0, mjcp, pll1_sysclk1, NULL, 0),
LPSC(41, 0, vpss_dac, pll1_sysclk3, vpss_dac_clkdev, 0),
{ }
};
static int dm355_psc_init(struct device *dev, void __iomem *base)
{
return davinci_psc_register_clocks(dev, dm355_psc_info, 42, base);
}
static struct clk_bulk_data dm355_psc_parent_clks[] = {
{ .id = "pll1_sysclk1" },
{ .id = "pll1_sysclk2" },
{ .id = "pll1_sysclk3" },
{ .id = "pll1_sysclk4" },
{ .id = "pll1_auxclk" },
};
const struct davinci_psc_init_data dm355_psc_init_data = {
.parent_clks = dm355_psc_parent_clks,
.num_parent_clks = ARRAY_SIZE(dm355_psc_parent_clks),
.psc_init = &dm355_psc_init,
};

View File

@ -0,0 +1,96 @@
// SPDX-License-Identifier: GPL-2.0
/*
* PSC clock descriptions for TI DaVinci DM365
*
* Copyright (C) 2018 David Lechner <david@lechnology.com>
*/
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include "psc.h"
LPSC_CLKDEV1(vpss_slave_clkdev, "slave", "vpss");
LPSC_CLKDEV1(spi1_clkdev, NULL, "spi_davinci.1");
LPSC_CLKDEV1(mmcsd1_clkdev, NULL, "da830-mmc.1");
LPSC_CLKDEV1(asp0_clkdev, NULL, "davinci-mcbsp");
LPSC_CLKDEV1(usb_clkdev, "usb", NULL);
LPSC_CLKDEV1(spi2_clkdev, NULL, "spi_davinci.2");
LPSC_CLKDEV1(aemif_clkdev, "aemif", NULL);
LPSC_CLKDEV1(mmcsd0_clkdev, NULL, "da830-mmc.0");
LPSC_CLKDEV1(i2c_clkdev, NULL, "i2c_davinci.1");
LPSC_CLKDEV1(uart0_clkdev, NULL, "serial8250.0");
LPSC_CLKDEV1(uart1_clkdev, NULL, "serial8250.1");
LPSC_CLKDEV1(spi0_clkdev, NULL, "spi_davinci.0");
/* REVISIT: gpio-davinci.c should be modified to drop con_id */
LPSC_CLKDEV1(gpio_clkdev, "gpio", NULL);
LPSC_CLKDEV1(timer0_clkdev, "timer0", NULL);
LPSC_CLKDEV1(timer2_clkdev, NULL, "davinci-wdt");
LPSC_CLKDEV1(spi3_clkdev, NULL, "spi_davinci.3");
LPSC_CLKDEV1(spi4_clkdev, NULL, "spi_davinci.4");
LPSC_CLKDEV2(emac_clkdev, NULL, "davinci_emac.1",
"fck", "davinci_mdio.0");
LPSC_CLKDEV1(voice_codec_clkdev, NULL, "davinci_voicecodec");
LPSC_CLKDEV1(vpss_dac_clkdev, "vpss_dac", NULL);
LPSC_CLKDEV1(vpss_master_clkdev, "master", "vpss");
static const struct davinci_lpsc_clk_info dm365_psc_info[] = {
LPSC(1, 0, vpss_slave, pll1_sysclk5, vpss_slave_clkdev, 0),
LPSC(5, 0, timer3, pll1_auxclk, NULL, 0),
LPSC(6, 0, spi1, pll1_sysclk4, spi1_clkdev, 0),
LPSC(7, 0, mmcsd1, pll1_sysclk4, mmcsd1_clkdev, 0),
LPSC(8, 0, asp0, pll1_sysclk4, asp0_clkdev, 0),
LPSC(9, 0, usb, pll1_auxclk, usb_clkdev, 0),
LPSC(10, 0, pwm3, pll1_auxclk, NULL, 0),
LPSC(11, 0, spi2, pll1_sysclk4, spi2_clkdev, 0),
LPSC(12, 0, rto, pll1_sysclk4, NULL, 0),
LPSC(14, 0, aemif, pll1_sysclk4, aemif_clkdev, 0),
LPSC(15, 0, mmcsd0, pll1_sysclk8, mmcsd0_clkdev, 0),
LPSC(18, 0, i2c, pll1_auxclk, i2c_clkdev, 0),
LPSC(19, 0, uart0, pll1_auxclk, uart0_clkdev, 0),
LPSC(20, 0, uart1, pll1_sysclk4, uart1_clkdev, 0),
LPSC(22, 0, spi0, pll1_sysclk4, spi0_clkdev, 0),
LPSC(23, 0, pwm0, pll1_auxclk, NULL, 0),
LPSC(24, 0, pwm1, pll1_auxclk, NULL, 0),
LPSC(25, 0, pwm2, pll1_auxclk, NULL, 0),
LPSC(26, 0, gpio, pll1_sysclk4, gpio_clkdev, 0),
LPSC(27, 0, timer0, pll1_auxclk, timer0_clkdev, LPSC_ALWAYS_ENABLED),
LPSC(28, 0, timer1, pll1_auxclk, NULL, 0),
/* REVISIT: why can't this be disabled? */
LPSC(29, 0, timer2, pll1_auxclk, timer2_clkdev, LPSC_ALWAYS_ENABLED),
LPSC(31, 0, arm, pll2_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
LPSC(38, 0, spi3, pll1_sysclk4, spi3_clkdev, 0),
LPSC(39, 0, spi4, pll1_auxclk, spi4_clkdev, 0),
LPSC(40, 0, emac, pll2_sysclk4, emac_clkdev, 0),
LPSC(44, 1, voice_codec, pll1_sysclk3, voice_codec_clkdev, 0),
LPSC(46, 1, vpss_dac, pll1_sysclk3, vpss_dac_clkdev, 0),
LPSC(47, 0, vpss_master, pll1_sysclk5, vpss_master_clkdev, 0),
LPSC(50, 0, mjcp, pll1_sysclk3, NULL, 0),
{ }
};
static int dm365_psc_init(struct device *dev, void __iomem *base)
{
return davinci_psc_register_clocks(dev, dm365_psc_info, 52, base);
}
static struct clk_bulk_data dm365_psc_parent_clks[] = {
{ .id = "pll1_sysclk1" },
{ .id = "pll1_sysclk3" },
{ .id = "pll1_sysclk4" },
{ .id = "pll1_sysclk5" },
{ .id = "pll1_sysclk8" },
{ .id = "pll2_sysclk2" },
{ .id = "pll2_sysclk4" },
{ .id = "pll1_auxclk" },
};
const struct davinci_psc_init_data dm365_psc_init_data = {
.parent_clks = dm365_psc_parent_clks,
.num_parent_clks = ARRAY_SIZE(dm365_psc_parent_clks),
.psc_init = &dm365_psc_init,
};

View File

@ -0,0 +1,83 @@
// SPDX-License-Identifier: GPL-2.0
/*
* PSC clock descriptions for TI DaVinci DM644x
*
* Copyright (C) 2018 David Lechner <david@lechnology.com>
*/
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include "psc.h"
LPSC_CLKDEV1(vpss_master_clkdev, "master", "vpss");
LPSC_CLKDEV1(vpss_slave_clkdev, "slave", "vpss");
LPSC_CLKDEV2(emac_clkdev, NULL, "davinci_emac.1",
"fck", "davinci_mdio.0");
LPSC_CLKDEV1(usb_clkdev, "usb", NULL);
LPSC_CLKDEV1(ide_clkdev, NULL, "palm_bk3710");
LPSC_CLKDEV1(aemif_clkdev, "aemif", NULL);
LPSC_CLKDEV1(mmcsd_clkdev, NULL, "dm6441-mmc.0");
LPSC_CLKDEV1(asp0_clkdev, NULL, "davinci-mcbsp");
LPSC_CLKDEV1(i2c_clkdev, NULL, "i2c_davinci.1");
LPSC_CLKDEV1(uart0_clkdev, NULL, "serial8250.0");
LPSC_CLKDEV1(uart1_clkdev, NULL, "serial8250.1");
LPSC_CLKDEV1(uart2_clkdev, NULL, "serial8250.2");
/* REVISIT: gpio-davinci.c should be modified to drop con_id */
LPSC_CLKDEV1(gpio_clkdev, "gpio", NULL);
LPSC_CLKDEV1(timer0_clkdev, "timer0", NULL);
LPSC_CLKDEV1(timer2_clkdev, NULL, "davinci-wdt");
static const struct davinci_lpsc_clk_info dm644x_psc_info[] = {
LPSC(0, 0, vpss_master, pll1_sysclk3, vpss_master_clkdev, 0),
LPSC(1, 0, vpss_slave, pll1_sysclk3, vpss_slave_clkdev, 0),
LPSC(6, 0, emac, pll1_sysclk5, emac_clkdev, 0),
LPSC(9, 0, usb, pll1_sysclk5, usb_clkdev, 0),
LPSC(10, 0, ide, pll1_sysclk5, ide_clkdev, 0),
LPSC(11, 0, vlynq, pll1_sysclk5, NULL, 0),
LPSC(14, 0, aemif, pll1_sysclk5, aemif_clkdev, 0),
LPSC(15, 0, mmcsd, pll1_sysclk5, mmcsd_clkdev, 0),
LPSC(17, 0, asp0, pll1_sysclk5, asp0_clkdev, 0),
LPSC(18, 0, i2c, pll1_auxclk, i2c_clkdev, 0),
LPSC(19, 0, uart0, pll1_auxclk, uart0_clkdev, 0),
LPSC(20, 0, uart1, pll1_auxclk, uart1_clkdev, 0),
LPSC(21, 0, uart2, pll1_auxclk, uart2_clkdev, 0),
LPSC(22, 0, spi, pll1_sysclk5, NULL, 0),
LPSC(23, 0, pwm0, pll1_auxclk, NULL, 0),
LPSC(24, 0, pwm1, pll1_auxclk, NULL, 0),
LPSC(25, 0, pwm2, pll1_auxclk, NULL, 0),
LPSC(26, 0, gpio, pll1_sysclk5, gpio_clkdev, 0),
LPSC(27, 0, timer0, pll1_auxclk, timer0_clkdev, LPSC_ALWAYS_ENABLED),
LPSC(28, 0, timer1, pll1_auxclk, NULL, 0),
/* REVISIT: why can't this be disabled? */
LPSC(29, 0, timer2, pll1_auxclk, timer2_clkdev, LPSC_ALWAYS_ENABLED),
LPSC(31, 0, arm, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
/* REVISIT how to disable? */
LPSC(39, 1, dsp, pll1_sysclk1, NULL, LPSC_ALWAYS_ENABLED),
/* REVISIT how to disable? */
LPSC(40, 1, vicp, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
{ }
};
static int dm644x_psc_init(struct device *dev, void __iomem *base)
{
return davinci_psc_register_clocks(dev, dm644x_psc_info, 41, base);
}
static struct clk_bulk_data dm644x_psc_parent_clks[] = {
{ .id = "pll1_sysclk1" },
{ .id = "pll1_sysclk2" },
{ .id = "pll1_sysclk3" },
{ .id = "pll1_sysclk5" },
{ .id = "pll1_auxclk" },
};
const struct davinci_psc_init_data dm644x_psc_init_data = {
.parent_clks = dm644x_psc_parent_clks,
.num_parent_clks = ARRAY_SIZE(dm644x_psc_parent_clks),
.psc_init = &dm644x_psc_init,
};

View File

@ -0,0 +1,80 @@
// SPDX-License-Identifier: GPL-2.0
/*
* PSC clock descriptions for TI DaVinci DM646x
*
* Copyright (C) 2018 David Lechner <david@lechnology.com>
*/
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include "psc.h"
LPSC_CLKDEV1(ide_clkdev, NULL, "palm_bk3710");
LPSC_CLKDEV2(emac_clkdev, NULL, "davinci_emac.1",
"fck", "davinci_mdio.0");
LPSC_CLKDEV1(aemif_clkdev, "aemif", NULL);
LPSC_CLKDEV1(mcasp0_clkdev, NULL, "davinci-mcasp.0");
LPSC_CLKDEV1(mcasp1_clkdev, NULL, "davinci-mcasp.1");
LPSC_CLKDEV1(uart0_clkdev, NULL, "serial8250.0");
LPSC_CLKDEV1(uart1_clkdev, NULL, "serial8250.1");
LPSC_CLKDEV1(uart2_clkdev, NULL, "serial8250.2");
LPSC_CLKDEV1(i2c_clkdev, NULL, "i2c_davinci.1");
/* REVISIT: gpio-davinci.c should be modified to drop con_id */
LPSC_CLKDEV1(gpio_clkdev, "gpio", NULL);
LPSC_CLKDEV1(timer0_clkdev, "timer0", NULL);
static const struct davinci_lpsc_clk_info dm646x_psc_info[] = {
LPSC(0, 0, arm, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
/* REVISIT how to disable? */
LPSC(1, 0, dsp, pll1_sysclk1, NULL, LPSC_ALWAYS_ENABLED),
LPSC(4, 0, edma_cc, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
LPSC(5, 0, edma_tc0, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
LPSC(6, 0, edma_tc1, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
LPSC(7, 0, edma_tc2, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
LPSC(8, 0, edma_tc3, pll1_sysclk2, NULL, LPSC_ALWAYS_ENABLED),
LPSC(10, 0, ide, pll1_sysclk4, ide_clkdev, 0),
LPSC(14, 0, emac, pll1_sysclk3, emac_clkdev, 0),
LPSC(16, 0, vpif0, ref_clk, NULL, LPSC_ALWAYS_ENABLED),
LPSC(17, 0, vpif1, ref_clk, NULL, LPSC_ALWAYS_ENABLED),
LPSC(21, 0, aemif, pll1_sysclk3, aemif_clkdev, LPSC_ALWAYS_ENABLED),
LPSC(22, 0, mcasp0, pll1_sysclk3, mcasp0_clkdev, 0),
LPSC(23, 0, mcasp1, pll1_sysclk3, mcasp1_clkdev, 0),
LPSC(26, 0, uart0, aux_clkin, uart0_clkdev, 0),
LPSC(27, 0, uart1, aux_clkin, uart1_clkdev, 0),
LPSC(28, 0, uart2, aux_clkin, uart2_clkdev, 0),
/* REVIST: disabling hangs system */
LPSC(29, 0, pwm0, pll1_sysclk3, NULL, LPSC_ALWAYS_ENABLED),
/* REVIST: disabling hangs system */
LPSC(30, 0, pwm1, pll1_sysclk3, NULL, LPSC_ALWAYS_ENABLED),
LPSC(31, 0, i2c, pll1_sysclk3, i2c_clkdev, 0),
LPSC(33, 0, gpio, pll1_sysclk3, gpio_clkdev, 0),
LPSC(34, 0, timer0, pll1_sysclk3, timer0_clkdev, LPSC_ALWAYS_ENABLED),
LPSC(35, 0, timer1, pll1_sysclk3, NULL, 0),
{ }
};
static int dm646x_psc_init(struct device *dev, void __iomem *base)
{
return davinci_psc_register_clocks(dev, dm646x_psc_info, 46, base);
}
static struct clk_bulk_data dm646x_psc_parent_clks[] = {
{ .id = "ref_clk" },
{ .id = "aux_clkin" },
{ .id = "pll1_sysclk1" },
{ .id = "pll1_sysclk2" },
{ .id = "pll1_sysclk3" },
{ .id = "pll1_sysclk4" },
{ .id = "pll1_sysclk5" },
};
const struct davinci_psc_init_data dm646x_psc_init_data = {
.parent_clks = dm646x_psc_parent_clks,
.num_parent_clks = ARRAY_SIZE(dm646x_psc_parent_clks),
.psc_init = &dm646x_psc_init,
};

550
drivers/clk/davinci/psc.c Normal file
View File

@ -0,0 +1,550 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Clock driver for TI Davinci PSC controllers
*
* Copyright (C) 2017 David Lechner <david@lechnology.com>
*
* Based on: drivers/clk/keystone/gate.c
* Copyright (C) 2013 Texas Instruments.
* Murali Karicheri <m-karicheri2@ti.com>
* Santosh Shilimkar <santosh.shilimkar@ti.com>
*
* And: arch/arm/mach-davinci/psc.c
* Copyright (C) 2006 Texas Instruments.
*/
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/err.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_clock.h>
#include <linux/pm_domain.h>
#include <linux/regmap.h>
#include <linux/reset-controller.h>
#include <linux/slab.h>
#include <linux/types.h>
#include "psc.h"
/* PSC register offsets */
#define EPCPR 0x070
#define PTCMD 0x120
#define PTSTAT 0x128
#define PDSTAT(n) (0x200 + 4 * (n))
#define PDCTL(n) (0x300 + 4 * (n))
#define MDSTAT(n) (0x800 + 4 * (n))
#define MDCTL(n) (0xa00 + 4 * (n))
/* PSC module states */
enum davinci_lpsc_state {
LPSC_STATE_SWRSTDISABLE = 0,
LPSC_STATE_SYNCRST = 1,
LPSC_STATE_DISABLE = 2,
LPSC_STATE_ENABLE = 3,
};
#define MDSTAT_STATE_MASK GENMASK(5, 0)
#define MDSTAT_MCKOUT BIT(12)
#define PDSTAT_STATE_MASK GENMASK(4, 0)
#define MDCTL_FORCE BIT(31)
#define MDCTL_LRESET BIT(8)
#define PDCTL_EPCGOOD BIT(8)
#define PDCTL_NEXT BIT(0)
struct davinci_psc_data {
struct clk_onecell_data clk_data;
struct genpd_onecell_data pm_data;
struct reset_controller_dev rcdev;
};
/**
* struct davinci_lpsc_clk - LPSC clock structure
* @dev: the device that provides this LPSC
* @hw: clk_hw for the LPSC
* @pm_domain: power domain for the LPSC
* @genpd_clk: clock reference owned by @pm_domain
* @regmap: PSC MMIO region
* @md: Module domain (LPSC module id)
* @pd: Power domain
* @flags: LPSC_* quirk flags
*/
struct davinci_lpsc_clk {
struct device *dev;
struct clk_hw hw;
struct generic_pm_domain pm_domain;
struct clk *genpd_clk;
struct regmap *regmap;
u32 md;
u32 pd;
u32 flags;
};
#define to_davinci_psc_data(x) container_of(x, struct davinci_psc_data, x)
#define to_davinci_lpsc_clk(x) container_of(x, struct davinci_lpsc_clk, x)
/**
* best_dev_name - get the "best" device name.
* @dev: the device
*
* Returns the device tree compatible name if the device has a DT node,
* otherwise return the device name. This is mainly needed because clkdev
* lookups are limited to 20 chars for dev_id and when using device tree,
* dev_name(dev) is much longer than that.
*/
static inline const char *best_dev_name(struct device *dev)
{
const char *compatible;
if (!of_property_read_string(dev->of_node, "compatible", &compatible))
return compatible;
return dev_name(dev);
}
static void davinci_lpsc_config(struct davinci_lpsc_clk *lpsc,
enum davinci_lpsc_state next_state)
{
u32 epcpr, pdstat, mdstat, ptstat;
regmap_write_bits(lpsc->regmap, MDCTL(lpsc->md), MDSTAT_STATE_MASK,
next_state);
if (lpsc->flags & LPSC_FORCE)
regmap_write_bits(lpsc->regmap, MDCTL(lpsc->md), MDCTL_FORCE,
MDCTL_FORCE);
regmap_read(lpsc->regmap, PDSTAT(lpsc->pd), &pdstat);
if ((pdstat & PDSTAT_STATE_MASK) == 0) {
regmap_write_bits(lpsc->regmap, PDCTL(lpsc->pd), PDCTL_NEXT,
PDCTL_NEXT);
regmap_write(lpsc->regmap, PTCMD, BIT(lpsc->pd));
regmap_read_poll_timeout(lpsc->regmap, EPCPR, epcpr,
epcpr & BIT(lpsc->pd), 0, 0);
regmap_write_bits(lpsc->regmap, PDCTL(lpsc->pd), PDCTL_EPCGOOD,
PDCTL_EPCGOOD);
} else {
regmap_write(lpsc->regmap, PTCMD, BIT(lpsc->pd));
}
regmap_read_poll_timeout(lpsc->regmap, PTSTAT, ptstat,
!(ptstat & BIT(lpsc->pd)), 0, 0);
regmap_read_poll_timeout(lpsc->regmap, MDSTAT(lpsc->md), mdstat,
(mdstat & MDSTAT_STATE_MASK) == next_state,
0, 0);
}
static int davinci_lpsc_clk_enable(struct clk_hw *hw)
{
struct davinci_lpsc_clk *lpsc = to_davinci_lpsc_clk(hw);
davinci_lpsc_config(lpsc, LPSC_STATE_ENABLE);
return 0;
}
static void davinci_lpsc_clk_disable(struct clk_hw *hw)
{
struct davinci_lpsc_clk *lpsc = to_davinci_lpsc_clk(hw);
davinci_lpsc_config(lpsc, LPSC_STATE_DISABLE);
}
static int davinci_lpsc_clk_is_enabled(struct clk_hw *hw)
{
struct davinci_lpsc_clk *lpsc = to_davinci_lpsc_clk(hw);
u32 mdstat;
regmap_read(lpsc->regmap, MDSTAT(lpsc->md), &mdstat);
return (mdstat & MDSTAT_MCKOUT) ? 1 : 0;
}
static const struct clk_ops davinci_lpsc_clk_ops = {
.enable = davinci_lpsc_clk_enable,
.disable = davinci_lpsc_clk_disable,
.is_enabled = davinci_lpsc_clk_is_enabled,
};
static int davinci_psc_genpd_attach_dev(struct generic_pm_domain *pm_domain,
struct device *dev)
{
struct davinci_lpsc_clk *lpsc = to_davinci_lpsc_clk(pm_domain);
struct clk *clk;
int ret;
/*
* pm_clk_remove_clk() will call clk_put(), so we have to use clk_get()
* to get the clock instead of using lpsc->hw.clk directly.
*/
clk = clk_get_sys(best_dev_name(lpsc->dev), clk_hw_get_name(&lpsc->hw));
if (IS_ERR(clk))
return (PTR_ERR(clk));
ret = pm_clk_create(dev);
if (ret < 0)
goto fail_clk_put;
ret = pm_clk_add_clk(dev, clk);
if (ret < 0)
goto fail_pm_clk_destroy;
lpsc->genpd_clk = clk;
return 0;
fail_pm_clk_destroy:
pm_clk_destroy(dev);
fail_clk_put:
clk_put(clk);
return ret;
}
static void davinci_psc_genpd_detach_dev(struct generic_pm_domain *pm_domain,
struct device *dev)
{
struct davinci_lpsc_clk *lpsc = to_davinci_lpsc_clk(pm_domain);
pm_clk_remove_clk(dev, lpsc->genpd_clk);
pm_clk_destroy(dev);
lpsc->genpd_clk = NULL;
}
/**
* davinci_lpsc_clk_register - register LPSC clock
* @name: name of this clock
* @parent_name: name of clock's parent
* @regmap: PSC MMIO region
* @md: local PSC number
* @pd: power domain
* @flags: LPSC_* flags
*/
static struct davinci_lpsc_clk *
davinci_lpsc_clk_register(struct device *dev, const char *name,
const char *parent_name, struct regmap *regmap,
u32 md, u32 pd, u32 flags)
{
struct clk_init_data init;
struct davinci_lpsc_clk *lpsc;
int ret;
bool is_on;
lpsc = devm_kzalloc(dev, sizeof(*lpsc), GFP_KERNEL);
if (!lpsc)
return ERR_PTR(-ENOMEM);
init.name = name;
init.ops = &davinci_lpsc_clk_ops;
init.parent_names = (parent_name ? &parent_name : NULL);
init.num_parents = (parent_name ? 1 : 0);
init.flags = 0;
if (flags & LPSC_ALWAYS_ENABLED)
init.flags |= CLK_IS_CRITICAL;
if (flags & LPSC_SET_RATE_PARENT)
init.flags |= CLK_SET_RATE_PARENT;
lpsc->dev = dev;
lpsc->regmap = regmap;
lpsc->hw.init = &init;
lpsc->md = md;
lpsc->pd = pd;
lpsc->flags = flags;
ret = devm_clk_hw_register(dev, &lpsc->hw);
if (ret < 0)
return ERR_PTR(ret);
/* genpd attach needs a way to look up this clock */
ret = clk_hw_register_clkdev(&lpsc->hw, name, best_dev_name(dev));
lpsc->pm_domain.name = devm_kasprintf(dev, GFP_KERNEL, "%s: %s",
best_dev_name(dev), name);
lpsc->pm_domain.attach_dev = davinci_psc_genpd_attach_dev;
lpsc->pm_domain.detach_dev = davinci_psc_genpd_detach_dev;
lpsc->pm_domain.flags = GENPD_FLAG_PM_CLK;
is_on = davinci_lpsc_clk_is_enabled(&lpsc->hw);
pm_genpd_init(&lpsc->pm_domain, NULL, is_on);
return lpsc;
}
static int davinci_lpsc_clk_reset(struct clk *clk, bool reset)
{
struct clk_hw *hw = __clk_get_hw(clk);
struct davinci_lpsc_clk *lpsc = to_davinci_lpsc_clk(hw);
u32 mdctl;
if (IS_ERR_OR_NULL(lpsc))
return -EINVAL;
mdctl = reset ? 0 : MDCTL_LRESET;
regmap_write_bits(lpsc->regmap, MDCTL(lpsc->md), MDCTL_LRESET, mdctl);
return 0;
}
/*
* REVISIT: These exported functions can be removed after a non-DT lookup is
* added to the reset controller framework and the davinci-rproc driver is
* updated to use the generic reset controller framework.
*/
int davinci_clk_reset_assert(struct clk *clk)
{
return davinci_lpsc_clk_reset(clk, true);
}
EXPORT_SYMBOL(davinci_clk_reset_assert);
int davinci_clk_reset_deassert(struct clk *clk)
{
return davinci_lpsc_clk_reset(clk, false);
}
EXPORT_SYMBOL(davinci_clk_reset_deassert);
static int davinci_psc_reset_assert(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct davinci_psc_data *psc = to_davinci_psc_data(rcdev);
struct clk *clk = psc->clk_data.clks[id];
return davinci_lpsc_clk_reset(clk, true);
}
static int davinci_psc_reset_deassert(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct davinci_psc_data *psc = to_davinci_psc_data(rcdev);
struct clk *clk = psc->clk_data.clks[id];
return davinci_lpsc_clk_reset(clk, false);
}
static const struct reset_control_ops davinci_psc_reset_ops = {
.assert = davinci_psc_reset_assert,
.deassert = davinci_psc_reset_deassert,
};
static int davinci_psc_reset_of_xlate(struct reset_controller_dev *rcdev,
const struct of_phandle_args *reset_spec)
{
struct of_phandle_args clkspec = *reset_spec; /* discard const qualifier */
struct clk *clk;
struct clk_hw *hw;
struct davinci_lpsc_clk *lpsc;
/* the clock node is the same as the reset node */
clk = of_clk_get_from_provider(&clkspec);
if (IS_ERR(clk))
return PTR_ERR(clk);
hw = __clk_get_hw(clk);
lpsc = to_davinci_lpsc_clk(hw);
clk_put(clk);
/* not all modules support local reset */
if (!(lpsc->flags & LPSC_LOCAL_RESET))
return -EINVAL;
return lpsc->md;
}
static const struct regmap_config davinci_psc_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
};
static struct davinci_psc_data *
__davinci_psc_register_clocks(struct device *dev,
const struct davinci_lpsc_clk_info *info,
int num_clks,
void __iomem *base)
{
struct davinci_psc_data *psc;
struct clk **clks;
struct generic_pm_domain **pm_domains;
struct regmap *regmap;
int i, ret;
psc = devm_kzalloc(dev, sizeof(*psc), GFP_KERNEL);
if (!psc)
return ERR_PTR(-ENOMEM);
clks = devm_kmalloc_array(dev, num_clks, sizeof(*clks), GFP_KERNEL);
if (!clks)
return ERR_PTR(-ENOMEM);
psc->clk_data.clks = clks;
psc->clk_data.clk_num = num_clks;
/*
* init array with error so that of_clk_src_onecell_get() doesn't
* return NULL for gaps in the sparse array
*/
for (i = 0; i < num_clks; i++)
clks[i] = ERR_PTR(-ENOENT);
pm_domains = devm_kcalloc(dev, num_clks, sizeof(*pm_domains), GFP_KERNEL);
if (!pm_domains)
return ERR_PTR(-ENOMEM);
psc->pm_data.domains = pm_domains;
psc->pm_data.num_domains = num_clks;
regmap = devm_regmap_init_mmio(dev, base, &davinci_psc_regmap_config);
if (IS_ERR(regmap))
return ERR_CAST(regmap);
for (; info->name; info++) {
struct davinci_lpsc_clk *lpsc;
lpsc = davinci_lpsc_clk_register(dev, info->name, info->parent,
regmap, info->md, info->pd,
info->flags);
if (IS_ERR(lpsc)) {
dev_warn(dev, "Failed to register %s (%ld)\n",
info->name, PTR_ERR(lpsc));
continue;
}
clks[info->md] = lpsc->hw.clk;
pm_domains[info->md] = &lpsc->pm_domain;
}
psc->rcdev.ops = &davinci_psc_reset_ops;
psc->rcdev.owner = THIS_MODULE;
psc->rcdev.of_node = dev->of_node;
psc->rcdev.of_reset_n_cells = 1;
psc->rcdev.of_xlate = davinci_psc_reset_of_xlate;
psc->rcdev.nr_resets = num_clks;
ret = devm_reset_controller_register(dev, &psc->rcdev);
if (ret < 0)
dev_warn(dev, "Failed to register reset controller (%d)\n", ret);
return psc;
}
int davinci_psc_register_clocks(struct device *dev,
const struct davinci_lpsc_clk_info *info,
u8 num_clks,
void __iomem *base)
{
struct davinci_psc_data *psc;
psc = __davinci_psc_register_clocks(dev, info, num_clks, base);
if (IS_ERR(psc))
return PTR_ERR(psc);
for (; info->name; info++) {
const struct davinci_lpsc_clkdev_info *cdevs = info->cdevs;
struct clk *clk = psc->clk_data.clks[info->md];
if (!cdevs || IS_ERR_OR_NULL(clk))
continue;
for (; cdevs->con_id || cdevs->dev_id; cdevs++)
clk_register_clkdev(clk, cdevs->con_id, cdevs->dev_id);
}
return 0;
}
int of_davinci_psc_clk_init(struct device *dev,
const struct davinci_lpsc_clk_info *info,
u8 num_clks,
void __iomem *base)
{
struct device_node *node = dev->of_node;
struct davinci_psc_data *psc;
psc = __davinci_psc_register_clocks(dev, info, num_clks, base);
if (IS_ERR(psc))
return PTR_ERR(psc);
of_genpd_add_provider_onecell(node, &psc->pm_data);
of_clk_add_provider(node, of_clk_src_onecell_get, &psc->clk_data);
return 0;
}
static const struct of_device_id davinci_psc_of_match[] = {
{ .compatible = "ti,da850-psc0", .data = &of_da850_psc0_init_data },
{ .compatible = "ti,da850-psc1", .data = &of_da850_psc1_init_data },
{ }
};
static const struct platform_device_id davinci_psc_id_table[] = {
{ .name = "da830-psc0", .driver_data = (kernel_ulong_t)&da830_psc0_init_data },
{ .name = "da830-psc1", .driver_data = (kernel_ulong_t)&da830_psc1_init_data },
{ .name = "da850-psc0", .driver_data = (kernel_ulong_t)&da850_psc0_init_data },
{ .name = "da850-psc1", .driver_data = (kernel_ulong_t)&da850_psc1_init_data },
{ .name = "dm355-psc", .driver_data = (kernel_ulong_t)&dm355_psc_init_data },
{ .name = "dm365-psc", .driver_data = (kernel_ulong_t)&dm365_psc_init_data },
{ .name = "dm644x-psc", .driver_data = (kernel_ulong_t)&dm644x_psc_init_data },
{ .name = "dm646x-psc", .driver_data = (kernel_ulong_t)&dm646x_psc_init_data },
{ }
};
static int davinci_psc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct of_device_id *of_id;
const struct davinci_psc_init_data *init_data = NULL;
struct resource *res;
void __iomem *base;
int ret;
of_id = of_match_device(davinci_psc_of_match, dev);
if (of_id)
init_data = of_id->data;
else if (pdev->id_entry)
init_data = (void *)pdev->id_entry->driver_data;
if (!init_data) {
dev_err(dev, "unable to find driver init data\n");
return -EINVAL;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
ret = devm_clk_bulk_get(dev, init_data->num_parent_clks,
init_data->parent_clks);
if (ret < 0)
return ret;
return init_data->psc_init(dev, base);
}
static struct platform_driver davinci_psc_driver = {
.probe = davinci_psc_probe,
.driver = {
.name = "davinci-psc-clk",
.of_match_table = davinci_psc_of_match,
},
.id_table = davinci_psc_id_table,
};
static int __init davinci_psc_driver_init(void)
{
return platform_driver_register(&davinci_psc_driver);
}
/* has to be postcore_initcall because davinci_gpio depend on PSC clocks */
postcore_initcall(davinci_psc_driver_init);

108
drivers/clk/davinci/psc.h Normal file
View File

@ -0,0 +1,108 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Clock driver for TI Davinci PSC controllers
*
* Copyright (C) 2018 David Lechner <david@lechnology.com>
*/
#ifndef __CLK_DAVINCI_PSC_H__
#define __CLK_DAVINCI_PSC_H__
#include <linux/clk-provider.h>
#include <linux/types.h>
/* PSC quirk flags */
#define LPSC_ALWAYS_ENABLED BIT(0) /* never disable this clock */
#define LPSC_SET_RATE_PARENT BIT(1) /* propagate set_rate to parent clock */
#define LPSC_FORCE BIT(2) /* requires MDCTL FORCE bit */
#define LPSC_LOCAL_RESET BIT(3) /* acts as reset provider */
struct davinci_lpsc_clkdev_info {
const char *con_id;
const char *dev_id;
};
#define LPSC_CLKDEV(c, d) { \
.con_id = (c), \
.dev_id = (d) \
}
#define LPSC_CLKDEV1(n, c, d) \
static const struct davinci_lpsc_clkdev_info n[] __initconst = { \
LPSC_CLKDEV((c), (d)), \
{ } \
}
#define LPSC_CLKDEV2(n, c1, d1, c2, d2) \
static const struct davinci_lpsc_clkdev_info n[] __initconst = { \
LPSC_CLKDEV((c1), (d1)), \
LPSC_CLKDEV((c2), (d2)), \
{ } \
}
#define LPSC_CLKDEV3(n, c1, d1, c2, d2, c3, d3) \
static const struct davinci_lpsc_clkdev_info n[] __initconst = { \
LPSC_CLKDEV((c1), (d1)), \
LPSC_CLKDEV((c2), (d2)), \
LPSC_CLKDEV((c3), (d3)), \
{ } \
}
/**
* davinci_lpsc_clk_info - LPSC module-specific clock information
* @name: the clock name
* @parent: the parent clock name
* @cdevs: optional array of clkdev lookup table info
* @md: the local module domain (LPSC id)
* @pd: the power domain id
* @flags: bitmask of LPSC_* flags
*/
struct davinci_lpsc_clk_info {
const char *name;
const char *parent;
const struct davinci_lpsc_clkdev_info *cdevs;
u32 md;
u32 pd;
unsigned long flags;
};
#define LPSC(m, d, n, p, c, f) \
{ \
.name = #n, \
.parent = #p, \
.cdevs = (c), \
.md = (m), \
.pd = (d), \
.flags = (f), \
}
int davinci_psc_register_clocks(struct device *dev,
const struct davinci_lpsc_clk_info *info,
u8 num_clks,
void __iomem *base);
int of_davinci_psc_clk_init(struct device *dev,
const struct davinci_lpsc_clk_info *info,
u8 num_clks,
void __iomem *base);
/* Device-specific data */
struct davinci_psc_init_data {
struct clk_bulk_data *parent_clks;
int num_parent_clks;
int (*psc_init)(struct device *dev, void __iomem *base);
};
extern const struct davinci_psc_init_data da830_psc0_init_data;
extern const struct davinci_psc_init_data da830_psc1_init_data;
extern const struct davinci_psc_init_data da850_psc0_init_data;
extern const struct davinci_psc_init_data da850_psc1_init_data;
extern const struct davinci_psc_init_data of_da850_psc0_init_data;
extern const struct davinci_psc_init_data of_da850_psc1_init_data;
extern const struct davinci_psc_init_data dm355_psc_init_data;
extern const struct davinci_psc_init_data dm365_psc_init_data;
extern const struct davinci_psc_init_data dm644x_psc_init_data;
extern const struct davinci_psc_init_data dm646x_psc_init_data;
#endif /* __CLK_DAVINCI_PSC_H__ */

View File

@ -25,6 +25,8 @@ struct rockchip_mmc_clock {
void __iomem *reg;
int id;
int shift;
int cached_phase;
struct notifier_block clk_rate_change_nb;
};
#define to_mmc_clock(_hw) container_of(_hw, struct rockchip_mmc_clock, hw)
@ -58,6 +60,12 @@ static int rockchip_mmc_get_phase(struct clk_hw *hw)
u16 degrees;
u32 delay_num = 0;
/* See the comment for rockchip_mmc_set_phase below */
if (!rate) {
pr_err("%s: invalid clk rate\n", __func__);
return -EINVAL;
}
raw_value = readl(mmc_clock->reg) >> (mmc_clock->shift);
degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90;
@ -84,6 +92,23 @@ static int rockchip_mmc_set_phase(struct clk_hw *hw, int degrees)
u32 raw_value;
u32 delay;
/*
* The below calculation is based on the output clock from
* MMC host to the card, which expects the phase clock inherits
* the clock rate from its parent, namely the output clock
* provider of MMC host. However, things may go wrong if
* (1) It is orphan.
* (2) It is assigned to the wrong parent.
*
* This check help debug the case (1), which seems to be the
* most likely problem we often face and which makes it difficult
* for people to debug unstable mmc tuning results.
*/
if (!rate) {
pr_err("%s: invalid clk rate\n", __func__);
return -EINVAL;
}
nineties = degrees / 90;
remainder = (degrees % 90);
@ -139,6 +164,41 @@ static const struct clk_ops rockchip_mmc_clk_ops = {
.set_phase = rockchip_mmc_set_phase,
};
#define to_rockchip_mmc_clock(x) \
container_of(x, struct rockchip_mmc_clock, clk_rate_change_nb)
static int rockchip_mmc_clk_rate_notify(struct notifier_block *nb,
unsigned long event, void *data)
{
struct rockchip_mmc_clock *mmc_clock = to_rockchip_mmc_clock(nb);
struct clk_notifier_data *ndata = data;
/*
* rockchip_mmc_clk is mostly used by mmc controllers to sample
* the intput data, which expects the fixed phase after the tuning
* process. However if the clock rate is changed, the phase is stale
* and may break the data sampling. So here we try to restore the phase
* for that case, except that
* (1) cached_phase is invaild since we inevitably cached it when the
* clock provider be reparented from orphan to its real parent in the
* first place. Otherwise we may mess up the initialization of MMC cards
* since we only set the default sample phase and drive phase later on.
* (2) the new coming rate is higher than the older one since mmc driver
* set the max-frequency to match the boards' ability but we can't go
* over the heads of that, otherwise the tests smoke out the issue.
*/
if (ndata->old_rate <= ndata->new_rate)
return NOTIFY_DONE;
if (event == PRE_RATE_CHANGE)
mmc_clock->cached_phase =
rockchip_mmc_get_phase(&mmc_clock->hw);
else if (mmc_clock->cached_phase != -EINVAL &&
event == POST_RATE_CHANGE)
rockchip_mmc_set_phase(&mmc_clock->hw, mmc_clock->cached_phase);
return NOTIFY_DONE;
}
struct clk *rockchip_clk_register_mmc(const char *name,
const char *const *parent_names, u8 num_parents,
void __iomem *reg, int shift)
@ -146,6 +206,7 @@ struct clk *rockchip_clk_register_mmc(const char *name,
struct clk_init_data init;
struct rockchip_mmc_clock *mmc_clock;
struct clk *clk;
int ret;
mmc_clock = kmalloc(sizeof(*mmc_clock), GFP_KERNEL);
if (!mmc_clock)
@ -162,8 +223,21 @@ struct clk *rockchip_clk_register_mmc(const char *name,
mmc_clock->shift = shift;
clk = clk_register(NULL, &mmc_clock->hw);
if (IS_ERR(clk))
kfree(mmc_clock);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
goto err_register;
}
mmc_clock->clk_rate_change_nb.notifier_call =
&rockchip_mmc_clk_rate_notify;
ret = clk_notifier_register(clk, &mmc_clock->clk_rate_change_nb);
if (ret)
goto err_notifier;
return clk;
err_notifier:
clk_unregister(clk);
err_register:
kfree(mmc_clock);
return ERR_PTR(ret);
}

View File

@ -387,7 +387,7 @@ static struct rockchip_clk_branch rk3228_clk_branches[] __initdata = {
RK2928_CLKSEL_CON(23), 5, 2, MFLAGS, 0, 6, DFLAGS,
RK2928_CLKGATE_CON(2), 15, GFLAGS),
COMPOSITE(SCLK_SDMMC, "sclk_sdmmc0", mux_mmc_src_p, 0,
COMPOSITE(SCLK_SDMMC, "sclk_sdmmc", mux_mmc_src_p, 0,
RK2928_CLKSEL_CON(11), 8, 2, MFLAGS, 0, 8, DFLAGS,
RK2928_CLKGATE_CON(2), 11, GFLAGS),

View File

@ -304,7 +304,7 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = {
COMPOSITE_NOMUX(0, "aclk_core", "armclk", CLK_IGNORE_UNUSED,
RK3328_CLKSEL_CON(1), 4, 3, DFLAGS | CLK_DIVIDER_READ_ONLY,
RK3328_CLKGATE_CON(7), 1, GFLAGS),
GATE(0, "aclk_core_niu", "aclk_core", CLK_IGNORE_UNUSED,
GATE(0, "aclk_core_niu", "aclk_core", 0,
RK3328_CLKGATE_CON(13), 0, GFLAGS),
GATE(0, "aclk_gic400", "aclk_core", CLK_IGNORE_UNUSED,
RK3328_CLKGATE_CON(13), 1, GFLAGS),
@ -318,7 +318,7 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = {
RK3328_CLKGATE_CON(6), 6, GFLAGS),
GATE(ACLK_GPU, "aclk_gpu", "aclk_gpu_pre", CLK_SET_RATE_PARENT,
RK3328_CLKGATE_CON(14), 0, GFLAGS),
GATE(0, "aclk_gpu_niu", "aclk_gpu_pre", CLK_IGNORE_UNUSED,
GATE(0, "aclk_gpu_niu", "aclk_gpu_pre", 0,
RK3328_CLKGATE_CON(14), 1, GFLAGS),
/* PD_DDR */
@ -513,9 +513,9 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = {
RK3328_CLKGATE_CON(24), 0, GFLAGS),
GATE(HCLK_RKVDEC, "hclk_rkvdec", "hclk_rkvdec_pre", CLK_SET_RATE_PARENT,
RK3328_CLKGATE_CON(24), 1, GFLAGS),
GATE(0, "aclk_rkvdec_niu", "aclk_rkvdec_pre", CLK_IGNORE_UNUSED,
GATE(0, "aclk_rkvdec_niu", "aclk_rkvdec_pre", 0,
RK3328_CLKGATE_CON(24), 2, GFLAGS),
GATE(0, "hclk_rkvdec_niu", "hclk_rkvdec_pre", CLK_IGNORE_UNUSED,
GATE(0, "hclk_rkvdec_niu", "hclk_rkvdec_pre", 0,
RK3328_CLKGATE_CON(24), 3, GFLAGS),
COMPOSITE(SCLK_VDEC_CABAC, "sclk_vdec_cabac", mux_4plls_p, 0,
@ -535,9 +535,9 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = {
RK3328_CLKGATE_CON(23), 0, GFLAGS),
GATE(HCLK_VPU, "hclk_vpu", "hclk_vpu_pre", CLK_SET_RATE_PARENT,
RK3328_CLKGATE_CON(23), 1, GFLAGS),
GATE(0, "aclk_vpu_niu", "aclk_vpu_pre", CLK_IGNORE_UNUSED,
GATE(0, "aclk_vpu_niu", "aclk_vpu_pre", 0,
RK3328_CLKGATE_CON(23), 2, GFLAGS),
GATE(0, "hclk_vpu_niu", "hclk_vpu_pre", CLK_IGNORE_UNUSED,
GATE(0, "hclk_vpu_niu", "hclk_vpu_pre", 0,
RK3328_CLKGATE_CON(23), 3, GFLAGS),
COMPOSITE(ACLK_RKVENC, "aclk_rkvenc", mux_4plls_p, 0,
@ -545,9 +545,9 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = {
RK3328_CLKGATE_CON(6), 3, GFLAGS),
FACTOR_GATE(HCLK_RKVENC, "hclk_rkvenc", "aclk_rkvenc", 0, 1, 4,
RK3328_CLKGATE_CON(11), 4, GFLAGS),
GATE(0, "aclk_rkvenc_niu", "aclk_rkvenc", CLK_IGNORE_UNUSED,
GATE(0, "aclk_rkvenc_niu", "aclk_rkvenc", 0,
RK3328_CLKGATE_CON(25), 0, GFLAGS),
GATE(0, "hclk_rkvenc_niu", "hclk_rkvenc", CLK_IGNORE_UNUSED,
GATE(0, "hclk_rkvenc_niu", "hclk_rkvenc", 0,
RK3328_CLKGATE_CON(25), 1, GFLAGS),
GATE(ACLK_H265, "aclk_h265", "aclk_rkvenc", 0,
RK3328_CLKGATE_CON(25), 0, GFLAGS),
@ -588,7 +588,7 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = {
COMPOSITE(ACLK_VOP_PRE, "aclk_vop_pre", mux_4plls_p, 0,
RK3328_CLKSEL_CON(39), 6, 2, MFLAGS, 0, 5, DFLAGS,
RK3328_CLKGATE_CON(5), 5, GFLAGS),
GATE(0, "clk_hdmi_sfc", "xin24m", 0,
GATE(SCLK_HDMI_SFC, "sclk_hdmi_sfc", "xin24m", 0,
RK3328_CLKGATE_CON(5), 4, GFLAGS),
COMPOSITE_NODIV(0, "clk_cif_src", mux_2plls_p, 0,
@ -602,7 +602,7 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = {
RK3328_CLKGATE_CON(5), 6, GFLAGS),
DIV(DCLK_HDMIPHY, "dclk_hdmiphy", "dclk_lcdc_src", 0,
RK3328_CLKSEL_CON(40), 3, 3, DFLAGS),
MUX(DCLK_LCDC, "dclk_lcdc", mux_dclk_lcdc_p, 0,
MUX(DCLK_LCDC, "dclk_lcdc", mux_dclk_lcdc_p, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT,
RK3328_CLKSEL_CON(40), 1, 1, MFLAGS),
/*
@ -709,14 +709,14 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = {
/* PD_VOP */
GATE(ACLK_RGA, "aclk_rga", "aclk_rga_pre", 0, RK3328_CLKGATE_CON(21), 10, GFLAGS),
GATE(0, "aclk_rga_niu", "aclk_rga_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(22), 3, GFLAGS),
GATE(0, "aclk_rga_niu", "aclk_rga_pre", 0, RK3328_CLKGATE_CON(22), 3, GFLAGS),
GATE(ACLK_VOP, "aclk_vop", "aclk_vop_pre", 0, RK3328_CLKGATE_CON(21), 2, GFLAGS),
GATE(0, "aclk_vop_niu", "aclk_vop_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(21), 4, GFLAGS),
GATE(0, "aclk_vop_niu", "aclk_vop_pre", 0, RK3328_CLKGATE_CON(21), 4, GFLAGS),
GATE(ACLK_IEP, "aclk_iep", "aclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 6, GFLAGS),
GATE(ACLK_CIF, "aclk_cif", "aclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 8, GFLAGS),
GATE(ACLK_HDCP, "aclk_hdcp", "aclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 15, GFLAGS),
GATE(0, "aclk_vio_niu", "aclk_vio_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(22), 2, GFLAGS),
GATE(0, "aclk_vio_niu", "aclk_vio_pre", 0, RK3328_CLKGATE_CON(22), 2, GFLAGS),
GATE(HCLK_VOP, "hclk_vop", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 3, GFLAGS),
GATE(0, "hclk_vop_niu", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 5, GFLAGS),
@ -724,10 +724,10 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = {
GATE(HCLK_CIF, "hclk_cif", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 9, GFLAGS),
GATE(HCLK_RGA, "hclk_rga", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 11, GFLAGS),
GATE(0, "hclk_ahb1tom", "hclk_vio_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(21), 12, GFLAGS),
GATE(0, "pclk_vio_h2p", "hclk_vio_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(21), 13, GFLAGS),
GATE(0, "hclk_vio_h2p", "hclk_vio_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(21), 14, GFLAGS),
GATE(0, "pclk_vio_h2p", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 13, GFLAGS),
GATE(0, "hclk_vio_h2p", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(21), 14, GFLAGS),
GATE(HCLK_HDCP, "hclk_hdcp", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(22), 0, GFLAGS),
GATE(HCLK_VIO, "hclk_vio", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(22), 1, GFLAGS),
GATE(0, "hclk_vio_niu", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(22), 1, GFLAGS),
GATE(PCLK_HDMI, "pclk_hdmi", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(22), 4, GFLAGS),
GATE(PCLK_HDCP, "pclk_hdcp", "hclk_vio_pre", 0, RK3328_CLKGATE_CON(22), 5, GFLAGS),
@ -743,19 +743,19 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = {
GATE(HCLK_HOST0_ARB, "hclk_host0_arb", "hclk_peri", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(19), 7, GFLAGS),
GATE(HCLK_OTG, "hclk_otg", "hclk_peri", 0, RK3328_CLKGATE_CON(19), 8, GFLAGS),
GATE(HCLK_OTG_PMU, "hclk_otg_pmu", "hclk_peri", 0, RK3328_CLKGATE_CON(19), 9, GFLAGS),
GATE(0, "hclk_peri_niu", "hclk_peri", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(19), 12, GFLAGS),
GATE(0, "pclk_peri_niu", "hclk_peri", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(19), 13, GFLAGS),
GATE(0, "hclk_peri_niu", "hclk_peri", 0, RK3328_CLKGATE_CON(19), 12, GFLAGS),
GATE(0, "pclk_peri_niu", "hclk_peri", 0, RK3328_CLKGATE_CON(19), 13, GFLAGS),
/* PD_GMAC */
GATE(ACLK_MAC2PHY, "aclk_mac2phy", "aclk_gmac", 0, RK3328_CLKGATE_CON(26), 0, GFLAGS),
GATE(ACLK_MAC2IO, "aclk_mac2io", "aclk_gmac", 0, RK3328_CLKGATE_CON(26), 2, GFLAGS),
GATE(0, "aclk_gmac_niu", "aclk_gmac", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(26), 4, GFLAGS),
GATE(0, "aclk_gmac_niu", "aclk_gmac", 0, RK3328_CLKGATE_CON(26), 4, GFLAGS),
GATE(PCLK_MAC2PHY, "pclk_mac2phy", "pclk_gmac", 0, RK3328_CLKGATE_CON(26), 1, GFLAGS),
GATE(PCLK_MAC2IO, "pclk_mac2io", "pclk_gmac", 0, RK3328_CLKGATE_CON(26), 3, GFLAGS),
GATE(0, "pclk_gmac_niu", "pclk_gmac", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(26), 5, GFLAGS),
GATE(0, "pclk_gmac_niu", "pclk_gmac", 0, RK3328_CLKGATE_CON(26), 5, GFLAGS),
/* PD_BUS */
GATE(0, "aclk_bus_niu", "aclk_bus_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 12, GFLAGS),
GATE(0, "aclk_bus_niu", "aclk_bus_pre", 0, RK3328_CLKGATE_CON(15), 12, GFLAGS),
GATE(ACLK_DCF, "aclk_dcf", "aclk_bus_pre", 0, RK3328_CLKGATE_CON(15), 11, GFLAGS),
GATE(ACLK_TSP, "aclk_tsp", "aclk_bus_pre", 0, RK3328_CLKGATE_CON(17), 12, GFLAGS),
GATE(0, "aclk_intmem", "aclk_bus_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 0, GFLAGS),
@ -769,10 +769,10 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = {
GATE(HCLK_TSP, "hclk_tsp", "hclk_bus_pre", 0, RK3328_CLKGATE_CON(17), 11, GFLAGS),
GATE(HCLK_CRYPTO_MST, "hclk_crypto_mst", "hclk_bus_pre", 0, RK3328_CLKGATE_CON(15), 7, GFLAGS),
GATE(HCLK_CRYPTO_SLV, "hclk_crypto_slv", "hclk_bus_pre", 0, RK3328_CLKGATE_CON(15), 8, GFLAGS),
GATE(0, "hclk_bus_niu", "hclk_bus_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 13, GFLAGS),
GATE(0, "hclk_bus_niu", "hclk_bus_pre", 0, RK3328_CLKGATE_CON(15), 13, GFLAGS),
GATE(HCLK_PDM, "hclk_pdm", "hclk_bus_pre", 0, RK3328_CLKGATE_CON(28), 0, GFLAGS),
GATE(0, "pclk_bus_niu", "pclk_bus", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 14, GFLAGS),
GATE(0, "pclk_bus_niu", "pclk_bus", 0, RK3328_CLKGATE_CON(15), 14, GFLAGS),
GATE(0, "pclk_efuse", "pclk_bus", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 9, GFLAGS),
GATE(0, "pclk_otp", "pclk_bus", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(28), 4, GFLAGS),
GATE(PCLK_I2C0, "pclk_i2c0", "pclk_bus", 0, RK3328_CLKGATE_CON(15), 10, GFLAGS),
@ -807,37 +807,42 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = {
GATE(0, "pclk_acodecphy", "pclk_phy_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(17), 5, GFLAGS),
GATE(PCLK_HDMIPHY, "pclk_hdmiphy", "pclk_phy_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(17), 7, GFLAGS),
GATE(0, "pclk_vdacphy", "pclk_phy_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(17), 8, GFLAGS),
GATE(0, "pclk_phy_niu", "pclk_phy_pre", CLK_IGNORE_UNUSED, RK3328_CLKGATE_CON(15), 15, GFLAGS),
GATE(0, "pclk_phy_niu", "pclk_phy_pre", 0, RK3328_CLKGATE_CON(15), 15, GFLAGS),
/* PD_MMC */
MMC(SCLK_SDMMC_DRV, "sdmmc_drv", "sclk_sdmmc",
MMC(SCLK_SDMMC_DRV, "sdmmc_drv", "clk_sdmmc",
RK3328_SDMMC_CON0, 1),
MMC(SCLK_SDMMC_SAMPLE, "sdmmc_sample", "sclk_sdmmc",
MMC(SCLK_SDMMC_SAMPLE, "sdmmc_sample", "clk_sdmmc",
RK3328_SDMMC_CON1, 1),
MMC(SCLK_SDIO_DRV, "sdio_drv", "sclk_sdio",
MMC(SCLK_SDIO_DRV, "sdio_drv", "clk_sdio",
RK3328_SDIO_CON0, 1),
MMC(SCLK_SDIO_SAMPLE, "sdio_sample", "sclk_sdio",
MMC(SCLK_SDIO_SAMPLE, "sdio_sample", "clk_sdio",
RK3328_SDIO_CON1, 1),
MMC(SCLK_EMMC_DRV, "emmc_drv", "sclk_emmc",
MMC(SCLK_EMMC_DRV, "emmc_drv", "clk_emmc",
RK3328_EMMC_CON0, 1),
MMC(SCLK_EMMC_SAMPLE, "emmc_sample", "sclk_emmc",
MMC(SCLK_EMMC_SAMPLE, "emmc_sample", "clk_emmc",
RK3328_EMMC_CON1, 1),
MMC(SCLK_SDMMC_EXT_DRV, "sdmmc_ext_drv", "sclk_sdmmc_ext",
MMC(SCLK_SDMMC_EXT_DRV, "sdmmc_ext_drv", "clk_sdmmc_ext",
RK3328_SDMMC_EXT_CON0, 1),
MMC(SCLK_SDMMC_EXT_SAMPLE, "sdmmc_ext_sample", "sclk_sdmmc_ext",
MMC(SCLK_SDMMC_EXT_SAMPLE, "sdmmc_ext_sample", "clk_sdmmc_ext",
RK3328_SDMMC_EXT_CON1, 1),
};
static const char *const rk3328_critical_clocks[] __initconst = {
"aclk_bus",
"aclk_bus_niu",
"pclk_bus",
"pclk_bus_niu",
"hclk_bus",
"hclk_bus_niu",
"aclk_peri",
"hclk_peri",
"hclk_peri_niu",
"pclk_peri",
"pclk_peri_niu",
"pclk_dbg",
"aclk_core_niu",
"aclk_gic400",
@ -861,6 +866,20 @@ static const char *const rk3328_critical_clocks[] __initconst = {
"aclk_rga_niu",
"pclk_vio_h2p",
"hclk_vio_h2p",
"aclk_vio_niu",
"hclk_vio_niu",
"aclk_vop_niu",
"hclk_vop_niu",
"aclk_gpu_niu",
"aclk_rkvdec_niu",
"hclk_rkvdec_niu",
"aclk_vpu_niu",
"hclk_vpu_niu",
"aclk_rkvenc_niu",
"hclk_rkvenc_niu",
"aclk_gmac_niu",
"pclk_gmac_niu",
"pclk_phy_niu",
};
static void __init rk3328_clk_init(struct device_node *np)

View File

@ -57,6 +57,7 @@ static struct rockchip_pll_rate_table rk3399_pll_rates[] = {
RK3036_PLL_RATE(1656000000, 1, 69, 1, 1, 1, 0),
RK3036_PLL_RATE(1632000000, 1, 68, 1, 1, 1, 0),
RK3036_PLL_RATE(1608000000, 1, 67, 1, 1, 1, 0),
RK3036_PLL_RATE(1600000000, 3, 200, 1, 1, 1, 0),
RK3036_PLL_RATE(1584000000, 1, 66, 1, 1, 1, 0),
RK3036_PLL_RATE(1560000000, 1, 65, 1, 1, 1, 0),
RK3036_PLL_RATE(1536000000, 1, 64, 1, 1, 1, 0),
@ -670,7 +671,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
RK3399_CLKGATE_CON(9), 7, GFLAGS,
&rk3399_uart3_fracmux),
COMPOSITE(0, "pclk_ddr", mux_pll_src_cpll_gpll_p, CLK_IGNORE_UNUSED,
COMPOSITE(PCLK_DDR, "pclk_ddr", mux_pll_src_cpll_gpll_p, CLK_IGNORE_UNUSED,
RK3399_CLKSEL_CON(6), 15, 1, MFLAGS, 8, 5, DFLAGS,
RK3399_CLKGATE_CON(3), 4, GFLAGS),
@ -886,7 +887,7 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
RK3399_CLKGATE_CON(31), 8, GFLAGS),
/* sdio & sdmmc */
COMPOSITE(0, "hclk_sd", mux_pll_src_cpll_gpll_p, 0,
COMPOSITE(HCLK_SD, "hclk_sd", mux_pll_src_cpll_gpll_p, 0,
RK3399_CLKSEL_CON(13), 15, 1, MFLAGS, 8, 5, DFLAGS,
RK3399_CLKGATE_CON(12), 13, GFLAGS),
GATE(HCLK_SDMMC, "hclk_sdmmc", "hclk_sd", 0,

View File

@ -57,6 +57,7 @@ static struct clk *rockchip_clk_register_branch(const char *name,
struct clk_divider *div = NULL;
const struct clk_ops *mux_ops = NULL, *div_ops = NULL,
*gate_ops = NULL;
int ret;
if (num_parents > 1) {
mux = kzalloc(sizeof(*mux), GFP_KERNEL);
@ -74,8 +75,10 @@ static struct clk *rockchip_clk_register_branch(const char *name,
if (gate_offset >= 0) {
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
if (!gate)
if (!gate) {
ret = -ENOMEM;
goto err_gate;
}
gate->flags = gate_flags;
gate->reg = base + gate_offset;
@ -86,8 +89,10 @@ static struct clk *rockchip_clk_register_branch(const char *name,
if (div_width > 0) {
div = kzalloc(sizeof(*div), GFP_KERNEL);
if (!div)
if (!div) {
ret = -ENOMEM;
goto err_div;
}
div->flags = div_flags;
div->reg = base + muxdiv_offset;
@ -106,12 +111,19 @@ static struct clk *rockchip_clk_register_branch(const char *name,
gate ? &gate->hw : NULL, gate_ops,
flags);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
goto err_composite;
}
return clk;
err_composite:
kfree(div);
err_div:
kfree(gate);
err_gate:
kfree(mux);
return ERR_PTR(-ENOMEM);
return ERR_PTR(ret);
}
struct rockchip_clk_frac {
@ -291,8 +303,10 @@ static struct clk *rockchip_clk_register_frac_branch(
init.num_parents = child->num_parents;
mux_clk = clk_register(NULL, &frac_mux->hw);
if (IS_ERR(mux_clk))
if (IS_ERR(mux_clk)) {
kfree(frac);
return clk;
}
rockchip_clk_add_lookup(ctx, mux_clk, child->id);

View File

@ -537,6 +537,8 @@ static void __init _ti_omap4_clkctrl_setup(struct device_node *node)
init.parent_names = &reg_data->parent;
init.num_parents = 1;
init.flags = 0;
if (reg_data->flags & CLKF_SET_RATE_PARENT)
init.flags |= CLK_SET_RATE_PARENT;
init.name = kasprintf(GFP_KERNEL, "%s:%s:%04x:%d",
node->parent->name, node->name,
reg_data->offset, 0);

View File

@ -76,6 +76,11 @@ enum {
#define CLKF_CORE (1 << 9)
#define CLKF_J_TYPE (1 << 10)
/* CLKCTRL flags */
#define CLKF_SW_SUP BIT(5)
#define CLKF_HW_SUP BIT(6)
#define CLKF_NO_IDLEST BIT(7)
#define CLK(dev, con, ck) \
{ \
.lk = { \
@ -185,10 +190,6 @@ extern const struct omap_clkctrl_data am438x_clkctrl_data[];
extern const struct omap_clkctrl_data dm814_clkctrl_data[];
extern const struct omap_clkctrl_data dm816_clkctrl_data[];
#define CLKF_SW_SUP BIT(0)
#define CLKF_HW_SUP BIT(1)
#define CLKF_NO_IDLEST BIT(2)
typedef void (*ti_of_clk_init_cb_t)(void *, struct device_node *);
struct clk *ti_clk_register(struct device *dev, struct clk_hw *hw,

View File

@ -57,6 +57,14 @@
#define UNIPHIER_PRO4_SYS_CLK_USB3(idx, ch) \
UNIPHIER_CLK_GATE("usb3" #ch, (idx), NULL, 0x2104, 16 + (ch))
#define UNIPHIER_PRO4_SYS_CLK_AIO(idx) \
UNIPHIER_CLK_FACTOR("aio-io200m", -1, "spll", 1, 8), \
UNIPHIER_CLK_GATE("aio", (idx), "aio-io200m", 0x2104, 13)
#define UNIPHIER_PRO5_SYS_CLK_AIO(idx) \
UNIPHIER_CLK_FACTOR("aio-io200m", -1, "spll", 1, 12), \
UNIPHIER_CLK_GATE("aio", (idx), "aio-io200m", 0x2104, 13)
#define UNIPHIER_LD11_SYS_CLK_AIO(idx) \
UNIPHIER_CLK_FACTOR("aio-io200m", -1, "spll", 1, 10), \
UNIPHIER_CLK_GATE("aio", (idx), "aio-io200m", 0x2108, 0)
@ -94,16 +102,22 @@ const struct uniphier_clk_data uniphier_pro4_sys_clk_data[] = {
UNIPHIER_CLK_FACTOR("upll", -1, "ref", 288, 25), /* 288 MHz */
UNIPHIER_CLK_FACTOR("a2pll", -1, "upll", 256, 125), /* 589.824 MHz */
UNIPHIER_CLK_FACTOR("vpll27a", -1, "ref", 270, 25), /* 270 MHz */
UNIPHIER_CLK_FACTOR("gpll", -1, "ref", 10, 1), /* 250 MHz */
UNIPHIER_CLK_FACTOR("uart", 0, "a2pll", 1, 8),
UNIPHIER_CLK_FACTOR("i2c", 1, "spll", 1, 32),
UNIPHIER_LD4_SYS_CLK_NAND(2),
UNIPHIER_LD4_SYS_CLK_SD,
UNIPHIER_CLK_FACTOR("usb2", -1, "upll", 1, 12),
UNIPHIER_PRO4_SYS_CLK_ETHER(6),
UNIPHIER_CLK_GATE("ether-gb", 7, "gpll", 0x2104, 5),
UNIPHIER_LD4_SYS_CLK_STDMAC(8), /* HSC, MIO, RLE */
UNIPHIER_CLK_GATE("ether-phy", 10, "ref", 0x2260, 0),
UNIPHIER_PRO4_SYS_CLK_GIO(12), /* Ether, SATA, USB3 */
UNIPHIER_PRO4_SYS_CLK_USB3(14, 0),
UNIPHIER_PRO4_SYS_CLK_USB3(15, 1),
UNIPHIER_CLK_GATE("sata0", 28, NULL, 0x2104, 18),
UNIPHIER_CLK_GATE("sata1", 29, NULL, 0x2104, 19),
UNIPHIER_PRO4_SYS_CLK_AIO(40),
{ /* sentinel */ }
};
@ -132,6 +146,8 @@ const struct uniphier_clk_data uniphier_pro5_sys_clk_data[] = {
UNIPHIER_PRO4_SYS_CLK_GIO(12), /* PCIe, USB3 */
UNIPHIER_PRO4_SYS_CLK_USB3(14, 0),
UNIPHIER_PRO4_SYS_CLK_USB3(15, 1),
UNIPHIER_CLK_GATE("pcie", 24, NULL, 0x2108, 2),
UNIPHIER_PRO5_SYS_CLK_AIO(40),
{ /* sentinel */ }
};
@ -149,6 +165,8 @@ const struct uniphier_clk_data uniphier_pxs2_sys_clk_data[] = {
/* The document mentions 0x2104 bit 18, but not functional */
UNIPHIER_CLK_GATE("usb30-phy", 16, NULL, 0x2104, 19),
UNIPHIER_CLK_GATE("usb31-phy", 20, NULL, 0x2104, 20),
UNIPHIER_CLK_GATE("sata0", 28, NULL, 0x2104, 22),
UNIPHIER_PRO5_SYS_CLK_AIO(40),
{ /* sentinel */ }
};
@ -205,6 +223,7 @@ const struct uniphier_clk_data uniphier_ld20_sys_clk_data[] = {
UNIPHIER_CLK_GATE("usb30", 14, NULL, 0x210c, 14),
UNIPHIER_CLK_GATE("usb30-phy0", 16, NULL, 0x210c, 12),
UNIPHIER_CLK_GATE("usb30-phy1", 17, NULL, 0x210c, 13),
UNIPHIER_CLK_GATE("pcie", 24, NULL, 0x210c, 4),
UNIPHIER_LD11_SYS_CLK_AIO(40),
UNIPHIER_LD11_SYS_CLK_EVEA(41),
UNIPHIER_LD11_SYS_CLK_EXIV(42),
@ -233,6 +252,8 @@ const struct uniphier_clk_data uniphier_pxs3_sys_clk_data[] = {
UNIPHIER_LD20_SYS_CLK_SD,
UNIPHIER_LD11_SYS_CLK_NAND(2),
UNIPHIER_LD11_SYS_CLK_EMMC(4),
UNIPHIER_CLK_GATE("ether0", 6, NULL, 0x210c, 9),
UNIPHIER_CLK_GATE("ether1", 7, NULL, 0x210c, 10),
UNIPHIER_CLK_GATE("usb30", 12, NULL, 0x210c, 4), /* =GIO0 */
UNIPHIER_CLK_GATE("usb31-0", 13, NULL, 0x210c, 5), /* =GIO1 */
UNIPHIER_CLK_GATE("usb31-1", 14, NULL, 0x210c, 6), /* =GIO1-1 */
@ -241,6 +262,10 @@ const struct uniphier_clk_data uniphier_pxs3_sys_clk_data[] = {
UNIPHIER_CLK_GATE("usb30-phy2", 18, NULL, 0x210c, 20),
UNIPHIER_CLK_GATE("usb31-phy0", 20, NULL, 0x210c, 17),
UNIPHIER_CLK_GATE("usb31-phy1", 21, NULL, 0x210c, 19),
UNIPHIER_CLK_GATE("pcie", 24, NULL, 0x210c, 3),
UNIPHIER_CLK_GATE("sata0", 28, NULL, 0x210c, 7),
UNIPHIER_CLK_GATE("sata1", 29, NULL, 0x210c, 8),
UNIPHIER_CLK_GATE("sata-phy", 30, NULL, 0x210c, 21),
/* CPU gears */
UNIPHIER_CLK_DIV4("cpll", 2, 3, 4, 8),
UNIPHIER_CLK_DIV4("spll", 2, 3, 4, 8),

View File

@ -193,7 +193,6 @@
#define HCLK_VPU_PRE 324
#define HCLK_VIO_PRE 325
#define HCLK_VPU 326
#define HCLK_VIO 327
#define HCLK_BUS_PRE 328
#define HCLK_PERI_PRE 329
#define HCLK_H264 330

View File

@ -0,0 +1,21 @@
// SPDX-License-Identifier: GPL-2.0
/*
* clk-da8xx-cfgchip - TI DaVinci DA8xx CFGCHIP clock driver
*
* Copyright (C) 2018 David Lechner <david@lechnology.com>
*/
#ifndef __LINUX_PLATFORM_DATA_CLK_DA8XX_CFGCHIP_H__
#define __LINUX_PLATFORM_DATA_CLK_DA8XX_CFGCHIP_H__
#include <linux/regmap.h>
/**
* da8xx_cfgchip_clk_platform_data
* @cfgchip: CFGCHIP syscon regmap
*/
struct da8xx_cfgchip_clk_platform_data {
struct regmap *cfgchip;
};
#endif /* __LINUX_PLATFORM_DATA_CLK_DA8XX_CFGCHIP_H__ */

View File

@ -0,0 +1,21 @@
// SPDX-License-Identifier: GPL-2.0
/*
* PLL clock driver for TI Davinci SoCs
*
* Copyright (C) 2018 David Lechner <david@lechnology.com>
*/
#ifndef __LINUX_PLATFORM_DATA_CLK_DAVINCI_PLL_H__
#define __LINUX_PLATFORM_DATA_CLK_DAVINCI_PLL_H__
#include <linux/regmap.h>
/**
* davinci_pll_platform_data
* @cfgchip: CFGCHIP syscon regmap
*/
struct davinci_pll_platform_data {
struct regmap *cfgchip;
};
#endif /* __LINUX_PLATFORM_DATA_CLK_DAVINCI_PLL_H__ */