From nobody Wed Feb 11 16:50:50 2026 Received: from mail-wm1-f50.google.com (mail-wm1-f50.google.com [209.85.128.50]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6A08319D8A5 for ; Mon, 24 Jun 2024 17:31:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.50 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719250277; cv=none; b=KhW9NiLYNI7j/8qHiSTyOcDWgU8QYoMIWkd/YEuKg3H+8L70/PVx+XsoCKsYeX2wbDv9CVjDtuv7bZ1m32nNa4OG33N14wHWs6TcSNVNY/K+7+JBqjwV9KubC4ugSjfG86OfinawnSIEcmuUvp/L1RyI31WbyAI3yHqwKL+YfEE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1719250277; c=relaxed/simple; bh=xOPNZnnN4l9IiDBBQWaVQUddXzvIyX6IwLvRqPfBopQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=QFYDHhfdfHTQHQqXbo6Bk0vcpe8OGklODhvxk0cPNwZkE57f5/Yefr2uyehQvI538BlWOyaKiaATusmELnztzUJ1HtMn+OVHS+CDHFCr5IedEAn4sPWRg/gxlOpRZKIbNbY5jP+i2l4ml6LqqATNAmYe6bDtkJX7ULYJYkzKsEA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=baylibre.com; spf=pass smtp.mailfrom=baylibre.com; dkim=pass (2048-bit key) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.b=CtCqyibD; arc=none smtp.client-ip=209.85.128.50 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=baylibre.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=baylibre.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=baylibre-com.20230601.gappssmtp.com header.i=@baylibre-com.20230601.gappssmtp.com header.b="CtCqyibD" Received: by mail-wm1-f50.google.com with SMTP id 5b1f17b1804b1-42198492353so38469865e9.1 for ; Mon, 24 Jun 2024 10:31:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=baylibre-com.20230601.gappssmtp.com; s=20230601; t=1719250274; x=1719855074; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=EyUzSKKBWAIU7D+W6BU/HhAkpHoZpwJrNeeENeOr2zc=; b=CtCqyibDuRUIHJBHFsDxaWqNh7rxuIU7rQo5fXgKEhPQ1nhK/2Wf6DzDXfBLfDn7tK zSwM0B+Mu3Z2uMdvcgfpUwL95QDkAaoXbSVGLkH9UjuqU/WPrGJv81rUXtNedUVLMjXT rtASs6Ex06nhyqoECYm6gbX6aXDGtSpRE984ZwPKne0W33kFGtRgGzsIMo331B22JnrC xubASwOWKUJV2PSof2qhZJEB/jfhaO89uo4seosQjR3mi9yAXm7s4Ya5dUbiPosD+no5 0hMRQHR72yDa0oKogbNuYYSS2XyZN6wXJ289+4Rvts/UmpMdYB4GNQrotN+T9d00dRD9 vyDw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1719250274; x=1719855074; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=EyUzSKKBWAIU7D+W6BU/HhAkpHoZpwJrNeeENeOr2zc=; b=ZleBInQqOQPPCHwXEE3+zml2yNweA8hKjW0BurF/f90BwNxoRhrjRRJjhbtbAvXvvA yXt3WeY+uP9oypyNzJQb9vldOpe03Knr/dvjxYkRlVSmrp7ZuAjsiZ4VWeBvvd7irdA/ 0e0tugC5yneFQJfIHgOW5ctqxDZLxvXRffYAIcL5kKBX6wInoFeJjRWNG325QheBdaV6 kS3Ia57CQ3eJDm8mlgEo1NBUkX5zGvej+qVZxIXLovmm4n3Vmw4fk8x9tEzBuMqD/b98 UhcBaPM7HqHikXCKe/ZFFAZz4to1nVKzIct3Tb6hUmFJDNQmNcwQa4Xi+JnPUzdcerTD swdA== X-Forwarded-Encrypted: i=1; AJvYcCURhxRlG+z9RPEjUc6EiYy+S3+udiELy94OI2ZFym9OeSfaoiu/1JaegF5irs6TzAznLo8swA8O43VcvYUbs4zH8j8c56xeIn+bwNR8 X-Gm-Message-State: AOJu0YxJWK12mzK/nr6K2r4TWWuVx9lu6aDgC193F/2Kj/uljH1JCey6 ugz43/k+Spkt5G1+GZRHh9xu70oZBYfJ/MQNFd6XMKCssOGIX0fM8Mx98mMbNnQ= X-Google-Smtp-Source: AGHT+IH9Le2Z83zzr03nxEAjY/rvbBfj6K+lspfdVSmGSmUqqLI6jwX/aUNW7OOKMxoc4Nmgu0ezRQ== X-Received: by 2002:a05:600c:1c1e:b0:424:7e1e:9080 with SMTP id 5b1f17b1804b1-4248cc2b868mr38774005e9.13.1719250273696; Mon, 24 Jun 2024 10:31:13 -0700 (PDT) Received: from toaster.lan ([2a01:e0a:3c5:5fb1:e4ee:e6f8:8fcc:a63b]) by smtp.googlemail.com with ESMTPSA id 5b1f17b1804b1-4247d210ff9sm183742385e9.39.2024.06.24.10.31.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 24 Jun 2024 10:31:13 -0700 (PDT) From: Jerome Brunet To: Jonathan Cameron , Lars-Peter Clausen , Neil Armstrong Cc: Jerome Brunet , Kevin Hilman , linux-kernel@vger.kernel.org, linux-amlogic@lists.infradead.org, linux-iio@vger.kernel.org, Rob Herring , Krzysztof Kozlowski , Conor Dooley Subject: [PATCH 2/2] iio: frequency: add amlogic clock measure support Date: Mon, 24 Jun 2024 19:31:03 +0200 Message-ID: <20240624173105.909554-3-jbrunet@baylibre.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240624173105.909554-1-jbrunet@baylibre.com> References: <20240624173105.909554-1-jbrunet@baylibre.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Patchwork-Bot: notify Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add support for the HW found in most Amlogic SoC dedicated to measure system clocks. This drivers aims to replace the one found in drivers/soc/amlogic/meson-clk-measure.c with following improvements: * Access to the measurements through the IIO API: Easier re-use of the results in userspace and other drivers * Controllable scale with raw measurements * Higher precision with processed measurements Signed-off-by: Jerome Brunet --- drivers/iio/frequency/Kconfig | 15 + drivers/iio/frequency/Makefile | 1 + drivers/iio/frequency/amlogic-clk-msr-io.c | 802 +++++++++++++++++++++ 3 files changed, 818 insertions(+) create mode 100644 drivers/iio/frequency/amlogic-clk-msr-io.c diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig index c455be7d4a1c..1682cbec0edf 100644 --- a/drivers/iio/frequency/Kconfig +++ b/drivers/iio/frequency/Kconfig @@ -7,6 +7,21 @@ # # When adding new entries keep the list in alphabetical order =20 +menu "Frequency Measurements" + +config AMLOGIC_CLK_MSR_IO + tristate "Amlogic IO Clock Measure" + default ARCH_MESON + depends on REGMAP_MMIO && COMMON_CLK + help + Say yes here to build support for Amlogic Clock Measure + HW found in Amlogic SoC, with IIO support + + To compile this driver as a module, choose M here: the + module will be called amlogic-clk-msr-io. + +endmenu + menu "Frequency Synthesizers DDS/PLL" =20 menu "Clock Generator/Distribution" diff --git a/drivers/iio/frequency/Makefile b/drivers/iio/frequency/Makefile index 70d0e0b70e80..3939970cccf3 100644 --- a/drivers/iio/frequency/Makefile +++ b/drivers/iio/frequency/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_ADMV1013) +=3D admv1013.o obj-$(CONFIG_ADMV1014) +=3D admv1014.o obj-$(CONFIG_ADMV4420) +=3D admv4420.o obj-$(CONFIG_ADRF6780) +=3D adrf6780.o +obj-$(CONFIG_AMLOGIC_CLK_MSR_IO) +=3D amlogic-clk-msr-io.o diff --git a/drivers/iio/frequency/amlogic-clk-msr-io.c b/drivers/iio/frequ= ency/amlogic-clk-msr-io.c new file mode 100644 index 000000000000..16a15dd346d8 --- /dev/null +++ b/drivers/iio/frequency/amlogic-clk-msr-io.c @@ -0,0 +1,802 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2024 BayLibre, SAS. +// Author: Jerome Brunet + +#include +#include +#include +#include +#include +#include + +#define MSR_CLK_REG0 0x0 +#define MSR_BUSY BIT(31) +#define MSR_CLK_SRC GENMASK(26, 20) +#define MSR_CLK_EN BIT(19) +#define MSR_CONT BIT(17) +#define MSR_ENABLE BIT(16) +#define MSR_TIME GENMASK(15, 0) +#define MSR_TIME_MAX (MSR_TIME + 1) +#define MSR_CLK_REG1 0x4 +#define MSR_CLK_REG2 0x8 +#define MSR_MEASURED GENMASK(19, 0) + +#define CLK_MIN_TIME 64 /* This allows to measure up to 8GHz */ +#define CLK_MSR_MAX 128 + +/* + * Note this driver aims to replace drivers/soc/amlogic/meson-clk-measure.c + * It provides the same functionality and adds support for the IIO API. + * The only thing missing is the clock summary which is very handy for + * debugging purpose. It is easy to reproduce the summary in userspace, + * from the iio device sysfs directory: + * + * for i in $(seq 0 127); do + * printf "%20s: %11dHz +/- %.0fHz\n" \ + * $(cat in_altvoltage${i}_label) \ + * $(cat in_altvoltage${i}_input) \ + * $(cat in_altvoltage_scale); + * done + */ + +struct amlogic_cmsr { + struct regmap *reg; + struct regmap *duty; + struct mutex lock; +}; + +static const char *cmsr_m8[CLK_MSR_MAX] =3D { + [0] =3D "ring_osc_out_ee0", + [1] =3D "ring_osc_out_ee1", + [2] =3D "ring_osc_out_ee2", + [3] =3D "a9_ring_osck", + [6] =3D "vid_pll", + [7] =3D "clk81", + [8] =3D "encp", + [9] =3D "encl", + [11] =3D "eth_rmii", + [13] =3D "amclk", + [14] =3D "fec_clk_0", + [15] =3D "fec_clk_1", + [16] =3D "fec_clk_2", + [18] =3D "a9_clk_div16", + [19] =3D "hdmi_sys", + [20] =3D "rtc_osc_clk_out", + [21] =3D "i2s_clk_in_src0", + [22] =3D "clk_rmii_from_pad", + [23] =3D "hdmi_ch0_tmds", + [24] =3D "lvds_fifo", + [26] =3D "sc_clk_int", + [28] =3D "sar_adc", + [30] =3D "mpll_clk_test_out", + [31] =3D "audac_clkpi", + [32] =3D "vdac", + [33] =3D "sdhc_rx", + [34] =3D "sdhc_sd", + [35] =3D "mali", + [36] =3D "hdmi_tx_pixel", + [38] =3D "vdin_meas", + [39] =3D "pcm_sclk", + [40] =3D "pcm_mclk", + [41] =3D "eth_rx_tx", + [42] =3D "pwm_d", + [43] =3D "pwm_c", + [44] =3D "pwm_b", + [45] =3D "pwm_a", + [46] =3D "pcm2_sclk", + [47] =3D "ddr_dpll_pt", + [48] =3D "pwm_f", + [49] =3D "pwm_e", + [59] =3D "hcodec", + [60] =3D "usb_32k_alt", + [61] =3D "gpio", + [62] =3D "vid2_pll", + [63] =3D "mipi_csi_cfg", +}; + +static const char *cmsr_gx[CLK_MSR_MAX] =3D { + [0] =3D "ring_osc_out_ee_0", + [1] =3D "ring_osc_out_ee_1", + [2] =3D "ring_osc_out_ee_2", + [3] =3D "a53_ring_osc", + [4] =3D "gp0_pll", + [6] =3D "enci", + [7] =3D "clk81", + [8] =3D "encp", + [9] =3D "encl", + [10] =3D "vdac", + [11] =3D "rgmii_tx", + [12] =3D "pdm", + [13] =3D "amclk", + [14] =3D "fec_0", + [15] =3D "fec_1", + [16] =3D "fec_2", + [17] =3D "sys_pll_div16", + [18] =3D "sys_cpu_div16", + [19] =3D "hdmitx_sys", + [20] =3D "rtc_osc_out", + [21] =3D "i2s_in_src0", + [22] =3D "eth_phy_ref", + [23] =3D "hdmi_todig", + [26] =3D "sc_int", + [28] =3D "sar_adc", + [31] =3D "mpll_test_out", + [32] =3D "vdec", + [35] =3D "mali", + [36] =3D "hdmi_tx_pixel", + [37] =3D "i958", + [38] =3D "vdin_meas", + [39] =3D "pcm_sclk", + [40] =3D "pcm_mclk", + [41] =3D "eth_rx_or_rmii", + [42] =3D "mp0_out", + [43] =3D "fclk_div5", + [44] =3D "pwm_b", + [45] =3D "pwm_a", + [46] =3D "vpu", + [47] =3D "ddr_dpll_pt", + [48] =3D "mp1_out", + [49] =3D "mp2_out", + [50] =3D "mp3_out", + [51] =3D "nand_core", + [52] =3D "sd_emmc_b", + [53] =3D "sd_emmc_a", + [55] =3D "vid_pll_div_out", + [56] =3D "cci", + [57] =3D "wave420l_c", + [58] =3D "wave420l_b", + [59] =3D "hcodec", + [60] =3D "alt_32k", + [61] =3D "gpio_msr", + [62] =3D "hevc", + [66] =3D "vid_lock", + [70] =3D "pwm_f", + [71] =3D "pwm_e", + [72] =3D "pwm_d", + [73] =3D "pwm_c", + [75] =3D "aoclkx2_int", + [76] =3D "aoclk_int", + [77] =3D "rng_ring_osc_0", + [78] =3D "rng_ring_osc_1", + [79] =3D "rng_ring_osc_2", + [80] =3D "rng_ring_osc_3", + [81] =3D "vapb", + [82] =3D "ge2d", +}; + +static const char *cmsr_axg[CLK_MSR_MAX] =3D { + [0] =3D "ring_osc_out_ee_0", + [1] =3D "ring_osc_out_ee_1", + [2] =3D "ring_osc_out_ee_2", + [3] =3D "a53_ring_osc", + [4] =3D "gp0_pll", + [5] =3D "gp1_pll", + [7] =3D "clk81", + [9] =3D "encl", + [17] =3D "sys_pll_div16", + [18] =3D "sys_cpu_div16", + [20] =3D "rtc_osc_out", + [23] =3D "mmc_clk", + [28] =3D "sar_adc", + [31] =3D "mpll_test_out", + [40] =3D "mod_eth_tx_clk", + [41] =3D "mod_eth_rx_clk_rmii", + [42] =3D "mp0_out", + [43] =3D "fclk_div5", + [44] =3D "pwm_b", + [45] =3D "pwm_a", + [46] =3D "vpu", + [47] =3D "ddr_dpll_pt", + [48] =3D "mp1_out", + [49] =3D "mp2_out", + [50] =3D "mp3_out", + [51] =3D "sd_emmm_c", + [52] =3D "sd_emmc_b", + [61] =3D "gpio_msr", + [66] =3D "audio_slv_lrclk_c", + [67] =3D "audio_slv_lrclk_b", + [68] =3D "audio_slv_lrclk_a", + [69] =3D "audio_slv_sclk_c", + [70] =3D "audio_slv_sclk_b", + [71] =3D "audio_slv_sclk_a", + [72] =3D "pwm_d", + [73] =3D "pwm_c", + [74] =3D "wifi_beacon", + [75] =3D "tdmin_lb_lrcl", + [76] =3D "tdmin_lb_sclk", + [77] =3D "rng_ring_osc_0", + [78] =3D "rng_ring_osc_1", + [79] =3D "rng_ring_osc_2", + [80] =3D "rng_ring_osc_3", + [81] =3D "vapb", + [82] =3D "ge2d", + [84] =3D "audio_resample", + [85] =3D "audio_pdm_sys", + [86] =3D "audio_spdifout", + [87] =3D "audio_spdifin", + [88] =3D "audio_lrclk_f", + [89] =3D "audio_lrclk_e", + [90] =3D "audio_lrclk_d", + [91] =3D "audio_lrclk_c", + [92] =3D "audio_lrclk_b", + [93] =3D "audio_lrclk_a", + [94] =3D "audio_sclk_f", + [95] =3D "audio_sclk_e", + [96] =3D "audio_sclk_d", + [97] =3D "audio_sclk_c", + [98] =3D "audio_sclk_b", + [99] =3D "audio_sclk_a", + [100] =3D "audio_mclk_f", + [101] =3D "audio_mclk_e", + [102] =3D "audio_mclk_d", + [103] =3D "audio_mclk_c", + [104] =3D "audio_mclk_b", + [105] =3D "audio_mclk_a", + [106] =3D "pcie_refclk_n", + [107] =3D "pcie_refclk_p", + [108] =3D "audio_locker_out", + [109] =3D "audio_locker_in", +}; + +static const char *cmsr_g12a[CLK_MSR_MAX] =3D { + [0] =3D "ring_osc_out_ee_0", + [1] =3D "ring_osc_out_ee_1", + [2] =3D "ring_osc_out_ee_2", + [3] =3D "sys_cpu_ring_osc", + [4] =3D "gp0_pll", + [6] =3D "enci", + [7] =3D "clk81", + [8] =3D "encp", + [9] =3D "encl", + [10] =3D "vdac", + [11] =3D "eth_tx", + [12] =3D "hifi_pll", + [13] =3D "mod_tcon", + [14] =3D "fec_0", + [15] =3D "fec_1", + [16] =3D "fec_2", + [17] =3D "sys_pll_div16", + [18] =3D "sys_cpu_div16", + [19] =3D "lcd_an_ph2", + [20] =3D "rtc_osc_out", + [21] =3D "lcd_an_ph3", + [22] =3D "eth_phy_ref", + [23] =3D "mpll_50m", + [24] =3D "eth_125m", + [25] =3D "eth_rmii", + [26] =3D "sc_int", + [27] =3D "in_mac", + [28] =3D "sar_adc", + [29] =3D "pcie_inp", + [30] =3D "pcie_inn", + [31] =3D "mpll_test_out", + [32] =3D "vdec", + [33] =3D "sys_cpu_ring_osc_1", + [34] =3D "eth_mpll_50m", + [35] =3D "mali", + [36] =3D "hdmi_tx_pixel", + [37] =3D "cdac", + [38] =3D "vdin_meas", + [39] =3D "bt656", + [41] =3D "eth_rx_or_rmii", + [42] =3D "mp0_out", + [43] =3D "fclk_div5", + [44] =3D "pwm_b", + [45] =3D "pwm_a", + [46] =3D "vpu", + [47] =3D "ddr_dpll_pt", + [48] =3D "mp1_out", + [49] =3D "mp2_out", + [50] =3D "mp3_out", + [51] =3D "sd_emmc_c", + [52] =3D "sd_emmc_b", + [53] =3D "sd_emmc_a", + [54] =3D "vpu_clkc", + [55] =3D "vid_pll_div_out", + [56] =3D "wave420l_a", + [57] =3D "wave420l_c", + [58] =3D "wave420l_b", + [59] =3D "hcodec", + [61] =3D "gpio_msr", + [62] =3D "hevcb", + [63] =3D "dsi_meas", + [64] =3D "spicc_1", + [65] =3D "spicc_0", + [66] =3D "vid_lock", + [67] =3D "dsi_phy", + [68] =3D "hdcp22_esm", + [69] =3D "hdcp22_skp", + [70] =3D "pwm_f", + [71] =3D "pwm_e", + [72] =3D "pwm_d", + [73] =3D "pwm_c", + [75] =3D "hevcf", + [77] =3D "rng_ring_osc_0", + [78] =3D "rng_ring_osc_1", + [79] =3D "rng_ring_osc_2", + [80] =3D "rng_ring_osc_3", + [81] =3D "vapb", + [82] =3D "ge2d", + [83] =3D "co_rx", + [84] =3D "co_tx", + [89] =3D "hdmi_todig", + [90] =3D "hdmitx_sys", + [91] =3D "sys_cpub_div16", + [92] =3D "sys_pll_cpub_div16", + [94] =3D "eth_phy_rx", + [95] =3D "eth_phy_pll", + [96] =3D "vpu_b", + [97] =3D "cpu_b_tmp", + [98] =3D "ts", + [99] =3D "ring_osc_out_ee_3", + [100] =3D "ring_osc_out_ee_4", + [101] =3D "ring_osc_out_ee_5", + [102] =3D "ring_osc_out_ee_6", + [103] =3D "ring_osc_out_ee_7", + [104] =3D "ring_osc_out_ee_8", + [105] =3D "ring_osc_out_ee_9", + [106] =3D "ephy_test", + [107] =3D "au_dac_g128x", + [108] =3D "audio_locker_out", + [109] =3D "audio_locker_in", + [110] =3D "audio_tdmout_c_sclk", + [111] =3D "audio_tdmout_b_sclk", + [112] =3D "audio_tdmout_a_sclk", + [113] =3D "audio_tdmin_lb_sclk", + [114] =3D "audio_tdmin_c_sclk", + [115] =3D "audio_tdmin_b_sclk", + [116] =3D "audio_tdmin_a_sclk", + [117] =3D "audio_resample", + [118] =3D "audio_pdm_sys", + [119] =3D "audio_spdifout_b", + [120] =3D "audio_spdifout", + [121] =3D "audio_spdifin", + [122] =3D "audio_pdm_dclk", +}; + +static const char *cmsr_sm1[CLK_MSR_MAX] =3D { + [0] =3D "ring_osc_out_ee_0", + [1] =3D "ring_osc_out_ee_1", + [2] =3D "ring_osc_out_ee_2", + [3] =3D "ring_osc_out_ee_3", + [4] =3D "gp0_pll", + [5] =3D "gp1_pll", + [6] =3D "enci", + [7] =3D "clk81", + [8] =3D "encp", + [9] =3D "encl", + [10] =3D "vdac", + [11] =3D "eth_tx", + [12] =3D "hifi_pll", + [13] =3D "mod_tcon", + [14] =3D "fec_0", + [15] =3D "fec_1", + [16] =3D "fec_2", + [17] =3D "sys_pll_div16", + [18] =3D "sys_cpu_div16", + [19] =3D "lcd_an_ph2", + [20] =3D "rtc_osc_out", + [21] =3D "lcd_an_ph3", + [22] =3D "eth_phy_ref", + [23] =3D "mpll_50m", + [24] =3D "eth_125m", + [25] =3D "eth_rmii", + [26] =3D "sc_int", + [27] =3D "in_mac", + [28] =3D "sar_adc", + [29] =3D "pcie_inp", + [30] =3D "pcie_inn", + [31] =3D "mpll_test_out", + [32] =3D "vdec", + [34] =3D "eth_mpll_50m", + [35] =3D "mali", + [36] =3D "hdmi_tx_pixel", + [37] =3D "cdac", + [38] =3D "vdin_meas", + [39] =3D "bt656", + [40] =3D "arm_ring_osc_out_4", + [41] =3D "eth_rx_or_rmii", + [42] =3D "mp0_out", + [43] =3D "fclk_div5", + [44] =3D "pwm_b", + [45] =3D "pwm_a", + [46] =3D "vpu", + [47] =3D "ddr_dpll_pt", + [48] =3D "mp1_out", + [49] =3D "mp2_out", + [50] =3D "mp3_out", + [51] =3D "sd_emmc_c", + [52] =3D "sd_emmc_b", + [53] =3D "sd_emmc_a", + [54] =3D "vpu_clkc", + [55] =3D "vid_pll_div_out", + [56] =3D "wave420l_a", + [57] =3D "wave420l_c", + [58] =3D "wave420l_b", + [59] =3D "hcodec", + [60] =3D "arm_ring_osc_out_5", + [61] =3D "gpio_msr", + [62] =3D "hevcb", + [63] =3D "dsi_meas", + [64] =3D "spicc_1", + [65] =3D "spicc_0", + [66] =3D "vid_lock", + [67] =3D "dsi_phy", + [68] =3D "hdcp22_esm", + [69] =3D "hdcp22_skp", + [70] =3D "pwm_f", + [71] =3D "pwm_e", + [72] =3D "pwm_d", + [73] =3D "pwm_c", + [74] =3D "arm_ring_osc_out_6", + [75] =3D "hevcf", + [76] =3D "arm_ring_osc_out_7", + [77] =3D "rng_ring_osc_0", + [78] =3D "rng_ring_osc_1", + [79] =3D "rng_ring_osc_2", + [80] =3D "rng_ring_osc_3", + [81] =3D "vapb", + [82] =3D "ge2d", + [83] =3D "co_rx", + [84] =3D "co_tx", + [85] =3D "arm_ring_osc_out_8", + [86] =3D "arm_ring_osc_out_9", + [87] =3D "mipi_dsi_phy", + [88] =3D "cis2_adapt", + [89] =3D "hdmi_todig", + [90] =3D "hdmitx_sys", + [91] =3D "nna_core", + [92] =3D "nna_axi", + [93] =3D "vad", + [94] =3D "eth_phy_rx", + [95] =3D "eth_phy_pll", + [96] =3D "vpu_b", + [97] =3D "cpu_b_tmp", + [98] =3D "ts", + [99] =3D "arm_ring_osc_out_10", + [100] =3D "arm_ring_osc_out_11", + [101] =3D "arm_ring_osc_out_12", + [102] =3D "arm_ring_osc_out_13", + [103] =3D "arm_ring_osc_out_14", + [104] =3D "arm_ring_osc_out_15", + [105] =3D "arm_ring_osc_out_16", + [106] =3D "ephy_test", + [107] =3D "au_dac_g128x", + [108] =3D "audio_locker_out", + [109] =3D "audio_locker_in", + [110] =3D "audio_tdmout_c_sclk", + [111] =3D "audio_tdmout_b_sclk", + [112] =3D "audio_tdmout_a_sclk", + [113] =3D "audio_tdmin_lb_sclk", + [114] =3D "audio_tdmin_c_sclk", + [115] =3D "audio_tdmin_b_sclk", + [116] =3D "audio_tdmin_a_sclk", + [117] =3D "audio_resample", + [118] =3D "audio_pdm_sys", + [119] =3D "audio_spdifout_b", + [120] =3D "audio_spdifout", + [121] =3D "audio_spdifin", + [122] =3D "audio_pdm_dclk", + [123] =3D "audio_resampled", + [124] =3D "earcrx_pll", + [125] =3D "earcrx_pll_test", + [126] =3D "csi_phy0", + [127] =3D "csi2_data", +}; + +static struct iio_chan_spec *cmsr_populate_channels(struct device *dev, + const char * const *conf) +{ + struct iio_chan_spec *chan; + int i; + + chan =3D devm_kzalloc(dev, sizeof(*chan) * CLK_MSR_MAX, GFP_KERNEL); + if (!chan) + return ERR_PTR(-ENOMEM); + + for (i =3D 0; i < CLK_MSR_MAX; i++) { + chan[i].type =3D IIO_ALTVOLTAGE; + chan[i].indexed =3D 1; + chan[i].channel =3D i; + chan[i].info_mask_separate =3D (BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_PROCESSED)); + chan[i].info_mask_shared_by_type =3D BIT(IIO_CHAN_INFO_SCALE); + chan[i].datasheet_name =3D conf[i]; + } + + return chan; +} + +static int cmsr_get_time_unlocked(struct amlogic_cmsr *cm) +{ + unsigned int raw; + + regmap_read(cm->reg, MSR_CLK_REG0, &raw); + + /* Field register value is time =3D val + 1 */ + return FIELD_GET(MSR_TIME, raw) + 1; +} + +static int cmsr_set_time_unlocked(struct amlogic_cmsr *cm, + unsigned int time) +{ + time -=3D 1; + + if (time < 0 || time > MSR_TIME) + return -EINVAL; + + regmap_update_bits(cm->reg, MSR_CLK_REG0, MSR_TIME, + FIELD_PREP(MSR_TIME, time)); + + return 0; +} + +static int cmsr_measure_unlocked(struct amlogic_cmsr *cm, + unsigned int idx) +{ + unsigned int raw; + int ret; + + regmap_update_bits(cm->reg, MSR_CLK_REG0, + MSR_ENABLE | MSR_CONT | MSR_CLK_EN | MSR_CLK_SRC, + MSR_CLK_EN | FIELD_PREP(MSR_CLK_SRC, idx)); + + regmap_set_bits(cm->reg, MSR_CLK_REG0, MSR_ENABLE); + ret =3D regmap_read_poll_timeout(cm->reg, MSR_CLK_REG0, raw, + !(raw & MSR_BUSY), 10, 70000); + regmap_clear_bits(cm->reg, MSR_CLK_REG0, MSR_ENABLE | MSR_CLK_EN); + + if (ret) + return ret; + + regmap_read(cm->reg, MSR_CLK_REG2, &raw); + ret =3D FIELD_GET(MSR_MEASURED, raw); + + /* Check for overflow */ + if (ret =3D=3D MSR_MEASURED) + return -EINVAL; + + return ret; +} + +static int cmsr_measure_processed_unlocked(struct amlogic_cmsr *cm, + unsigned int idx, + int *val2) +{ + unsigned int time =3D CLK_MIN_TIME; + u64 rate; + int ret; + + /* + * The challenge here is to provide the best accuracy + * while not spending to much time doing it. + * - Starting with a short duration risk not detecting + * slow clocks, but it is fast. All 128 can be done in ~8ms + * - Starting with a long duration risk overflowing the + * measurement counter and would be way to long, especially + * considering the number of disabled clocks. ~4s for all + * 128 worst case. + * + * This IP measures system clocks so all clock are expected + * to be 1kHz < f < 8GHz. We can compromise based on this, + * doing it in 3 pass: + * #1 Starting if 64us window: detects 30kHz < f < 8GHz + * - Go to #2 if no detection, Go to #3 otherwise + * #2 Extend duration to 1024us (f > 1kHz) + - Assume f =3D 0Hz if no detection, Go to #3 otherwise + * #3 Clock has been detected, adjust window for best accuracy + * + * Doing the range detection takes ~1ms per clock, including disabled + clocks. + * Actual measurement takes at most ~65ms in step #3 for slow clocks, + * when the full range the HW is used. + */ + + /* Step #1 - quick measurement */ + cmsr_set_time_unlocked(cm, time); + ret =3D cmsr_measure_unlocked(cm, idx); + if (ret < 0) + return ret; + + else if (ret =3D=3D 0) { + /* Step #2 - extend the window if necessary */ + time *=3D 16; + cmsr_set_time_unlocked(cm, time); + ret =3D cmsr_measure_unlocked(cm, idx); + if (ret < 0) + return ret; + + else if (ret =3D=3D 0) { + /* Still nothing - assume no clock */ + *val2 =3D 0; + return 0; + } + } + + /* Step #3: Adapt scale for better precision */ + time =3D time * MSR_MEASURED * 3 / (ret * 4); /* 25% margin */ + time =3D min_t(unsigned int, MSR_TIME_MAX, time); + + /* Actually measure rate with an optimized scale */ + cmsr_set_time_unlocked(cm, time); + ret =3D cmsr_measure_unlocked(cm, idx); + if (ret < 0) + return ret; + + rate =3D DIV_ROUND_CLOSEST_ULL(ret * 1000000ULL, time); + *val2 =3D rate >> 32ULL; + return (int)rate; +} + +static int cmsr_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct amlogic_cmsr *cm =3D iio_priv(indio_dev); + + guard(mutex)(&cm->lock); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + *val =3D cmsr_measure_unlocked(cm, chan->channel); + if (*val < 0) + return *val; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_PROCESSED: /* Result in Hz */ + *val =3D cmsr_measure_processed_unlocked(cm, chan->channel, val2); + if (*val < 0) + return *val; + return IIO_VAL_INT_64; + + case IIO_CHAN_INFO_SCALE: + *val2 =3D cmsr_get_time_unlocked(cm); + *val =3D 1000000; + return IIO_VAL_FRACTIONAL; + + default: + return -EINVAL; + } +} + +static int cmsr_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct amlogic_cmsr *cm =3D iio_priv(indio_dev); + unsigned int time; + + guard(mutex)(&cm->lock); + + switch (info) { + case IIO_CHAN_INFO_SCALE: + time =3D DIV_ROUND_CLOSEST(1000000U, val); + return cmsr_set_time_unlocked(cm, time); + default: + return -EINVAL; + } +} + +static int cmsr_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long info) +{ + switch (info) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT; + default: + return IIO_VAL_INT_PLUS_MICRO; + } +} + +static int cmsr_read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + char *label) +{ + return sprintf(label, "%s\n", chan->datasheet_name); +} + +static const struct of_device_id cmsr_of_match[] =3D { + { + .compatible =3D "amlogic,gx-clk-msr-io", + .data =3D cmsr_gx, + }, { + .compatible =3D "amlogic,meson8-clk-msr-io", + .data =3D cmsr_m8, + }, { + .compatible =3D "amlogic,axg-clk-msr-io", + .data =3D cmsr_axg, + }, { + .compatible =3D "amlogic,g12a-clk-msr-io", + .data =3D cmsr_g12a, + }, { + .compatible =3D "amlogic,sm1-clk-msr-io", + .data =3D cmsr_sm1, + }, {} +}; +MODULE_DEVICE_TABLE(of, cmsr_of_match); + +static const struct iio_info cmsr_info =3D { + .read_raw =3D cmsr_read_raw, + .read_label =3D cmsr_read_label, + .write_raw =3D cmsr_write_raw, + .write_raw_get_fmt =3D cmsr_write_raw_get_fmt, +}; + +static const struct regmap_config cmsr_regmap_cfg =3D { + .reg_bits =3D 32, + .val_bits =3D 32, + .reg_stride =3D 4, +}; + +static int cmsr_probe(struct platform_device *pdev) +{ + struct device *dev =3D &pdev->dev; + struct iio_dev *indio_dev; + struct amlogic_cmsr *cm; + const char * const *conf; + void __iomem *regs; + + indio_dev =3D devm_iio_device_alloc(dev, sizeof(*cm)); + if (!indio_dev) + return -ENOMEM; + platform_set_drvdata(pdev, indio_dev); + cm =3D iio_priv(indio_dev); + + conf =3D of_device_get_match_data(dev); + if (!conf) { + dev_err(dev, "failed to match device\n"); + return -ENODEV; + } + + regs =3D devm_platform_ioremap_resource_byname(pdev, "reg"); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + cm->reg =3D devm_regmap_init_mmio(dev, regs, &cmsr_regmap_cfg); + if (IS_ERR(cm->reg)) { + dev_err(dev, "failed to init main regmap: %ld\n", + PTR_ERR(cm->reg)); + return PTR_ERR(cm->reg); + } + + regs =3D devm_platform_ioremap_resource_byname(pdev, "duty"); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + cm->duty =3D devm_regmap_init_mmio(dev, regs, &cmsr_regmap_cfg); + if (IS_ERR(cm->duty)) { + dev_err(dev, "failed to init duty regmap: %ld\n", + PTR_ERR(cm->duty)); + return PTR_ERR(cm->duty); + } + + mutex_init(&cm->lock); + + /* Init scale with a sane default */ + cmsr_set_time_unlocked(cm, CLK_MIN_TIME); + + indio_dev->name =3D "amlogic-clk-msr"; + indio_dev->info =3D &cmsr_info; + indio_dev->modes =3D INDIO_DIRECT_MODE; + indio_dev->num_channels =3D CLK_MSR_MAX; + indio_dev->channels =3D cmsr_populate_channels(dev, conf); + if (IS_ERR(indio_dev->channels)) + return PTR_ERR(indio_dev->channels); + + return devm_iio_device_register(dev, indio_dev); +} + +static struct platform_driver amlogic_cmsr_driver =3D { + .probe =3D cmsr_probe, + .driver =3D { + .name =3D "amlogic-clk-msr-io", + .of_match_table =3D cmsr_of_match, + }, +}; +module_platform_driver(amlogic_cmsr_driver); + +MODULE_DESCRIPTION("Amlogic Clock Measure IO driver"); +MODULE_AUTHOR("Jerome Brunet "); +MODULE_LICENSE("GPL"); --=20 2.43.0