2018-12-13 15:43:05 +00:00
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2018 NXP
* Dong Aisheng < aisheng . dong @ nxp . com >
*/
# include <linux/clk-provider.h>
# include <linux/err.h>
# include <linux/io.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
# include "clk-scu.h"
# include "clk-imx8qxp-lpcg.h"
2018-12-23 16:20:02 +00:00
# include <dt-bindings/clock/imx8-clock.h>
2018-12-13 15:43:05 +00:00
/*
* struct imx8qxp_lpcg_data - Description of one LPCG clock
* @ id : clock ID
* @ name : clock name
* @ parent : parent clock name
* @ flags : common clock flags
* @ offset : offset of this LPCG clock
* @ bit_idx : bit index of this LPCG clock
* @ hw_gate : whether supports HW autogate
*
* This structure describes one LPCG clock
*/
struct imx8qxp_lpcg_data {
int id ;
char * name ;
char * parent ;
unsigned long flags ;
u32 offset ;
u8 bit_idx ;
bool hw_gate ;
} ;
/*
* struct imx8qxp_ss_lpcg - Description of one subsystem LPCG clocks
* @ lpcg : LPCG clocks array of one subsystem
* @ num_lpcg : the number of LPCG clocks
* @ num_max : the maximum number of LPCG clocks
*
* This structure describes each subsystem LPCG clocks information
* which then will be used to create respective LPCGs clocks
*/
struct imx8qxp_ss_lpcg {
const struct imx8qxp_lpcg_data * lpcg ;
u8 num_lpcg ;
u8 num_max ;
} ;
static const struct imx8qxp_lpcg_data imx8qxp_lpcg_adma [ ] = {
2018-12-23 16:20:02 +00:00
{ IMX_ADMA_LPCG_UART0_IPG_CLK , " uart0_lpcg_ipg_clk " , " dma_ipg_clk_root " , 0 , ADMA_LPUART_0_LPCG , 16 , 0 , } ,
{ IMX_ADMA_LPCG_UART0_BAUD_CLK , " uart0_lpcg_baud_clk " , " uart0_clk " , 0 , ADMA_LPUART_0_LPCG , 0 , 0 , } ,
{ IMX_ADMA_LPCG_UART1_IPG_CLK , " uart1_lpcg_ipg_clk " , " dma_ipg_clk_root " , 0 , ADMA_LPUART_1_LPCG , 16 , 0 , } ,
{ IMX_ADMA_LPCG_UART1_BAUD_CLK , " uart1_lpcg_baud_clk " , " uart1_clk " , 0 , ADMA_LPUART_1_LPCG , 0 , 0 , } ,
{ IMX_ADMA_LPCG_UART2_IPG_CLK , " uart2_lpcg_ipg_clk " , " dma_ipg_clk_root " , 0 , ADMA_LPUART_2_LPCG , 16 , 0 , } ,
{ IMX_ADMA_LPCG_UART2_BAUD_CLK , " uart2_lpcg_baud_clk " , " uart2_clk " , 0 , ADMA_LPUART_2_LPCG , 0 , 0 , } ,
{ IMX_ADMA_LPCG_UART3_IPG_CLK , " uart3_lpcg_ipg_clk " , " dma_ipg_clk_root " , 0 , ADMA_LPUART_3_LPCG , 16 , 0 , } ,
{ IMX_ADMA_LPCG_UART3_BAUD_CLK , " uart3_lpcg_baud_clk " , " uart3_clk " , 0 , ADMA_LPUART_3_LPCG , 0 , 0 , } ,
{ IMX_ADMA_LPCG_I2C0_IPG_CLK , " i2c0_lpcg_ipg_clk " , " dma_ipg_clk_root " , 0 , ADMA_LPI2C_0_LPCG , 16 , 0 , } ,
{ IMX_ADMA_LPCG_I2C0_CLK , " i2c0_lpcg_clk " , " i2c0_clk " , 0 , ADMA_LPI2C_0_LPCG , 0 , 0 , } ,
{ IMX_ADMA_LPCG_I2C1_IPG_CLK , " i2c1_lpcg_ipg_clk " , " dma_ipg_clk_root " , 0 , ADMA_LPI2C_1_LPCG , 16 , 0 , } ,
{ IMX_ADMA_LPCG_I2C1_CLK , " i2c1_lpcg_clk " , " i2c1_clk " , 0 , ADMA_LPI2C_1_LPCG , 0 , 0 , } ,
{ IMX_ADMA_LPCG_I2C2_IPG_CLK , " i2c2_lpcg_ipg_clk " , " dma_ipg_clk_root " , 0 , ADMA_LPI2C_2_LPCG , 16 , 0 , } ,
{ IMX_ADMA_LPCG_I2C2_CLK , " i2c2_lpcg_clk " , " i2c2_clk " , 0 , ADMA_LPI2C_2_LPCG , 0 , 0 , } ,
{ IMX_ADMA_LPCG_I2C3_IPG_CLK , " i2c3_lpcg_ipg_clk " , " dma_ipg_clk_root " , 0 , ADMA_LPI2C_3_LPCG , 16 , 0 , } ,
{ IMX_ADMA_LPCG_I2C3_CLK , " i2c3_lpcg_clk " , " i2c3_clk " , 0 , ADMA_LPI2C_3_LPCG , 0 , 0 , } ,
2019-07-02 18:20:07 +03:00
{ IMX_ADMA_LPCG_DSP_CORE_CLK , " dsp_lpcg_core_clk " , " dma_ipg_clk_root " , 0 , ADMA_HIFI_LPCG , 28 , 0 , } ,
{ IMX_ADMA_LPCG_DSP_IPG_CLK , " dsp_lpcg_ipg_clk " , " dma_ipg_clk_root " , 0 , ADMA_HIFI_LPCG , 20 , 0 , } ,
{ IMX_ADMA_LPCG_DSP_ADB_CLK , " dsp_lpcg_adb_clk " , " dma_ipg_clk_root " , 0 , ADMA_HIFI_LPCG , 16 , 0 , } ,
{ IMX_ADMA_LPCG_OCRAM_IPG_CLK , " ocram_lpcg_ipg_clk " , " dma_ipg_clk_root " , 0 , ADMA_OCRAM_LPCG , 16 , 0 , } ,
2018-12-13 15:43:05 +00:00
} ;
static const struct imx8qxp_ss_lpcg imx8qxp_ss_adma = {
. lpcg = imx8qxp_lpcg_adma ,
. num_lpcg = ARRAY_SIZE ( imx8qxp_lpcg_adma ) ,
2018-12-23 16:20:02 +00:00
. num_max = IMX_ADMA_LPCG_CLK_END ,
2018-12-13 15:43:05 +00:00
} ;
static const struct imx8qxp_lpcg_data imx8qxp_lpcg_conn [ ] = {
2018-12-23 16:20:02 +00:00
{ IMX_CONN_LPCG_SDHC0_PER_CLK , " sdhc0_lpcg_per_clk " , " sdhc0_clk " , 0 , CONN_USDHC_0_LPCG , 0 , 0 , } ,
{ IMX_CONN_LPCG_SDHC0_IPG_CLK , " sdhc0_lpcg_ipg_clk " , " conn_ipg_clk_root " , 0 , CONN_USDHC_0_LPCG , 16 , 0 , } ,
{ IMX_CONN_LPCG_SDHC0_HCLK , " sdhc0_lpcg_ahb_clk " , " conn_axi_clk_root " , 0 , CONN_USDHC_0_LPCG , 20 , 0 , } ,
{ IMX_CONN_LPCG_SDHC1_PER_CLK , " sdhc1_lpcg_per_clk " , " sdhc1_clk " , 0 , CONN_USDHC_1_LPCG , 0 , 0 , } ,
{ IMX_CONN_LPCG_SDHC1_IPG_CLK , " sdhc1_lpcg_ipg_clk " , " conn_ipg_clk_root " , 0 , CONN_USDHC_1_LPCG , 16 , 0 , } ,
{ IMX_CONN_LPCG_SDHC1_HCLK , " sdhc1_lpcg_ahb_clk " , " conn_axi_clk_root " , 0 , CONN_USDHC_1_LPCG , 20 , 0 , } ,
{ IMX_CONN_LPCG_SDHC2_PER_CLK , " sdhc2_lpcg_per_clk " , " sdhc2_clk " , 0 , CONN_USDHC_2_LPCG , 0 , 0 , } ,
{ IMX_CONN_LPCG_SDHC2_IPG_CLK , " sdhc2_lpcg_ipg_clk " , " conn_ipg_clk_root " , 0 , CONN_USDHC_2_LPCG , 16 , 0 , } ,
{ IMX_CONN_LPCG_SDHC2_HCLK , " sdhc2_lpcg_ahb_clk " , " conn_axi_clk_root " , 0 , CONN_USDHC_2_LPCG , 20 , 0 , } ,
{ IMX_CONN_LPCG_ENET0_ROOT_CLK , " enet0_ipg_root_clk " , " enet0_clk " , 0 , CONN_ENET_0_LPCG , 0 , 0 , } ,
{ IMX_CONN_LPCG_ENET0_TX_CLK , " enet0_tx_clk " , " enet0_clk " , 0 , CONN_ENET_0_LPCG , 4 , 0 , } ,
{ IMX_CONN_LPCG_ENET0_AHB_CLK , " enet0_ahb_clk " , " conn_axi_clk_root " , 0 , CONN_ENET_0_LPCG , 8 , 0 , } ,
{ IMX_CONN_LPCG_ENET0_IPG_S_CLK , " enet0_ipg_s_clk " , " conn_ipg_clk_root " , 0 , CONN_ENET_0_LPCG , 20 , 0 , } ,
{ IMX_CONN_LPCG_ENET0_IPG_CLK , " enet0_ipg_clk " , " enet0_ipg_s_clk " , 0 , CONN_ENET_0_LPCG , 16 , 0 , } ,
{ IMX_CONN_LPCG_ENET1_ROOT_CLK , " enet1_ipg_root_clk " , " enet1_clk " , 0 , CONN_ENET_1_LPCG , 0 , 0 , } ,
{ IMX_CONN_LPCG_ENET1_TX_CLK , " enet1_tx_clk " , " enet1_clk " , 0 , CONN_ENET_1_LPCG , 4 , 0 , } ,
{ IMX_CONN_LPCG_ENET1_AHB_CLK , " enet1_ahb_clk " , " conn_axi_clk_root " , 0 , CONN_ENET_1_LPCG , 8 , 0 , } ,
{ IMX_CONN_LPCG_ENET1_IPG_S_CLK , " enet1_ipg_s_clk " , " conn_ipg_clk_root " , 0 , CONN_ENET_1_LPCG , 20 , 0 , } ,
{ IMX_CONN_LPCG_ENET1_IPG_CLK , " enet1_ipg_clk " , " enet0_ipg_s_clk " , 0 , CONN_ENET_1_LPCG , 16 , 0 , } ,
2018-12-13 15:43:05 +00:00
} ;
static const struct imx8qxp_ss_lpcg imx8qxp_ss_conn = {
. lpcg = imx8qxp_lpcg_conn ,
. num_lpcg = ARRAY_SIZE ( imx8qxp_lpcg_conn ) ,
2018-12-23 16:20:02 +00:00
. num_max = IMX_CONN_LPCG_CLK_END ,
2018-12-13 15:43:05 +00:00
} ;
static const struct imx8qxp_lpcg_data imx8qxp_lpcg_lsio [ ] = {
2018-12-23 16:20:02 +00:00
{ IMX_LSIO_LPCG_PWM0_IPG_CLK , " pwm0_lpcg_ipg_clk " , " pwm0_clk " , 0 , LSIO_PWM_0_LPCG , 0 , 0 , } ,
{ IMX_LSIO_LPCG_PWM0_IPG_HF_CLK , " pwm0_lpcg_ipg_hf_clk " , " pwm0_clk " , 0 , LSIO_PWM_0_LPCG , 4 , 0 , } ,
{ IMX_LSIO_LPCG_PWM0_IPG_S_CLK , " pwm0_lpcg_ipg_s_clk " , " pwm0_clk " , 0 , LSIO_PWM_0_LPCG , 16 , 0 , } ,
{ IMX_LSIO_LPCG_PWM0_IPG_SLV_CLK , " pwm0_lpcg_ipg_slv_clk " , " lsio_bus_clk_root " , 0 , LSIO_PWM_0_LPCG , 20 , 0 , } ,
{ IMX_LSIO_LPCG_PWM0_IPG_MSTR_CLK , " pwm0_lpcg_ipg_mstr_clk " , " pwm0_clk " , 0 , LSIO_PWM_0_LPCG , 24 , 0 , } ,
{ IMX_LSIO_LPCG_PWM1_IPG_CLK , " pwm1_lpcg_ipg_clk " , " pwm1_clk " , 0 , LSIO_PWM_1_LPCG , 0 , 0 , } ,
{ IMX_LSIO_LPCG_PWM1_IPG_HF_CLK , " pwm1_lpcg_ipg_hf_clk " , " pwm1_clk " , 0 , LSIO_PWM_1_LPCG , 4 , 0 , } ,
{ IMX_LSIO_LPCG_PWM1_IPG_S_CLK , " pwm1_lpcg_ipg_s_clk " , " pwm1_clk " , 0 , LSIO_PWM_1_LPCG , 16 , 0 , } ,
{ IMX_LSIO_LPCG_PWM1_IPG_SLV_CLK , " pwm1_lpcg_ipg_slv_clk " , " lsio_bus_clk_root " , 0 , LSIO_PWM_1_LPCG , 20 , 0 , } ,
{ IMX_LSIO_LPCG_PWM1_IPG_MSTR_CLK , " pwm1_lpcg_ipg_mstr_clk " , " pwm1_clk " , 0 , LSIO_PWM_1_LPCG , 24 , 0 , } ,
{ IMX_LSIO_LPCG_PWM2_IPG_CLK , " pwm2_lpcg_ipg_clk " , " pwm2_clk " , 0 , LSIO_PWM_2_LPCG , 0 , 0 , } ,
{ IMX_LSIO_LPCG_PWM2_IPG_HF_CLK , " pwm2_lpcg_ipg_hf_clk " , " pwm2_clk " , 0 , LSIO_PWM_2_LPCG , 4 , 0 , } ,
{ IMX_LSIO_LPCG_PWM2_IPG_S_CLK , " pwm2_lpcg_ipg_s_clk " , " pwm2_clk " , 0 , LSIO_PWM_2_LPCG , 16 , 0 , } ,
{ IMX_LSIO_LPCG_PWM2_IPG_SLV_CLK , " pwm2_lpcg_ipg_slv_clk " , " lsio_bus_clk_root " , 0 , LSIO_PWM_2_LPCG , 20 , 0 , } ,
{ IMX_LSIO_LPCG_PWM2_IPG_MSTR_CLK , " pwm2_lpcg_ipg_mstr_clk " , " pwm2_clk " , 0 , LSIO_PWM_2_LPCG , 24 , 0 , } ,
{ IMX_LSIO_LPCG_PWM3_IPG_CLK , " pwm3_lpcg_ipg_clk " , " pwm3_clk " , 0 , LSIO_PWM_3_LPCG , 0 , 0 , } ,
{ IMX_LSIO_LPCG_PWM3_IPG_HF_CLK , " pwm3_lpcg_ipg_hf_clk " , " pwm3_clk " , 0 , LSIO_PWM_3_LPCG , 4 , 0 , } ,
{ IMX_LSIO_LPCG_PWM3_IPG_S_CLK , " pwm3_lpcg_ipg_s_clk " , " pwm3_clk " , 0 , LSIO_PWM_3_LPCG , 16 , 0 , } ,
{ IMX_LSIO_LPCG_PWM3_IPG_SLV_CLK , " pwm3_lpcg_ipg_slv_clk " , " lsio_bus_clk_root " , 0 , LSIO_PWM_3_LPCG , 20 , 0 , } ,
{ IMX_LSIO_LPCG_PWM3_IPG_MSTR_CLK , " pwm3_lpcg_ipg_mstr_clk " , " pwm3_clk " , 0 , LSIO_PWM_3_LPCG , 24 , 0 , } ,
{ IMX_LSIO_LPCG_PWM4_IPG_CLK , " pwm4_lpcg_ipg_clk " , " pwm4_clk " , 0 , LSIO_PWM_4_LPCG , 0 , 0 , } ,
{ IMX_LSIO_LPCG_PWM4_IPG_HF_CLK , " pwm4_lpcg_ipg_hf_clk " , " pwm4_clk " , 0 , LSIO_PWM_4_LPCG , 4 , 0 , } ,
{ IMX_LSIO_LPCG_PWM4_IPG_S_CLK , " pwm4_lpcg_ipg_s_clk " , " pwm4_clk " , 0 , LSIO_PWM_4_LPCG , 16 , 0 , } ,
{ IMX_LSIO_LPCG_PWM4_IPG_SLV_CLK , " pwm4_lpcg_ipg_slv_clk " , " lsio_bus_clk_root " , 0 , LSIO_PWM_4_LPCG , 20 , 0 , } ,
{ IMX_LSIO_LPCG_PWM4_IPG_MSTR_CLK , " pwm4_lpcg_ipg_mstr_clk " , " pwm4_clk " , 0 , LSIO_PWM_4_LPCG , 24 , 0 , } ,
{ IMX_LSIO_LPCG_PWM5_IPG_CLK , " pwm5_lpcg_ipg_clk " , " pwm5_clk " , 0 , LSIO_PWM_5_LPCG , 0 , 0 , } ,
{ IMX_LSIO_LPCG_PWM5_IPG_HF_CLK , " pwm5_lpcg_ipg_hf_clk " , " pwm5_clk " , 0 , LSIO_PWM_5_LPCG , 4 , 0 , } ,
{ IMX_LSIO_LPCG_PWM5_IPG_S_CLK , " pwm5_lpcg_ipg_s_clk " , " pwm5_clk " , 0 , LSIO_PWM_5_LPCG , 16 , 0 , } ,
{ IMX_LSIO_LPCG_PWM5_IPG_SLV_CLK , " pwm5_lpcg_ipg_slv_clk " , " lsio_bus_clk_root " , 0 , LSIO_PWM_5_LPCG , 20 , 0 , } ,
{ IMX_LSIO_LPCG_PWM5_IPG_MSTR_CLK , " pwm5_lpcg_ipg_mstr_clk " , " pwm5_clk " , 0 , LSIO_PWM_5_LPCG , 24 , 0 , } ,
{ IMX_LSIO_LPCG_PWM6_IPG_CLK , " pwm6_lpcg_ipg_clk " , " pwm6_clk " , 0 , LSIO_PWM_6_LPCG , 0 , 0 , } ,
{ IMX_LSIO_LPCG_PWM6_IPG_HF_CLK , " pwm6_lpcg_ipg_hf_clk " , " pwm6_clk " , 0 , LSIO_PWM_6_LPCG , 4 , 0 , } ,
{ IMX_LSIO_LPCG_PWM6_IPG_S_CLK , " pwm6_lpcg_ipg_s_clk " , " pwm6_clk " , 0 , LSIO_PWM_6_LPCG , 16 , 0 , } ,
{ IMX_LSIO_LPCG_PWM6_IPG_SLV_CLK , " pwm6_lpcg_ipg_slv_clk " , " lsio_bus_clk_root " , 0 , LSIO_PWM_6_LPCG , 20 , 0 , } ,
{ IMX_LSIO_LPCG_PWM6_IPG_MSTR_CLK , " pwm6_lpcg_ipg_mstr_clk " , " pwm6_clk " , 0 , LSIO_PWM_6_LPCG , 24 , 0 , } ,
2018-12-13 15:43:05 +00:00
} ;
static const struct imx8qxp_ss_lpcg imx8qxp_ss_lsio = {
. lpcg = imx8qxp_lpcg_lsio ,
. num_lpcg = ARRAY_SIZE ( imx8qxp_lpcg_lsio ) ,
2018-12-23 16:20:02 +00:00
. num_max = IMX_LSIO_LPCG_CLK_END ,
2018-12-13 15:43:05 +00:00
} ;
static int imx8qxp_lpcg_clk_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct device_node * np = dev - > of_node ;
struct clk_hw_onecell_data * clk_data ;
const struct imx8qxp_ss_lpcg * ss_lpcg ;
const struct imx8qxp_lpcg_data * lpcg ;
struct resource * res ;
struct clk_hw * * clks ;
void __iomem * base ;
int i ;
ss_lpcg = of_device_get_match_data ( dev ) ;
if ( ! ss_lpcg )
return - ENODEV ;
2019-12-09 22:56:28 +02:00
/*
* Please don ' t replace this with devm_platform_ioremap_resource .
*
* devm_platform_ioremap_resource calls devm_ioremap_resource which
* differs from devm_ioremap by also calling devm_request_mem_region
* and preventing other mappings in the same area .
*
* On imx8 the LPCG nodes map entire subsystems and overlap
* peripherals , this means that using devm_platform_ioremap_resource
* will cause many devices to fail to probe including serial ports .
*/
2018-12-13 15:43:05 +00:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
clk: imx: fix potential NULL dereference in imx8qxp_lpcg_clk_probe()
platform_get_resource() may fail and return NULL, so we should
better check it's return value to avoid a NULL pointer dereference
a bit later in the code.
This is detected by Coccinelle semantic patch.
@@
expression pdev, res, n, t, e, e1, e2;
@@
res = platform_get_resource(pdev, t, n);
+ if (!res)
+ return -EINVAL;
... when != res == NULL
e = devm_ioremap(e1, res->start, e2);
Fixes: 1e3121bfe51a ("clk: imx: add imx8qxp lpcg driver")
Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
Reviewed-by: Dong Aisheng <aisheng.dong@nxp.com>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
2018-12-18 06:43:09 +00:00
if ( ! res )
return - EINVAL ;
2018-12-13 15:43:05 +00:00
base = devm_ioremap ( dev , res - > start , resource_size ( res ) ) ;
if ( ! base )
return - ENOMEM ;
clk_data = devm_kzalloc ( & pdev - > dev , struct_size ( clk_data , hws ,
ss_lpcg - > num_max ) , GFP_KERNEL ) ;
if ( ! clk_data )
return - ENOMEM ;
clk_data - > num = ss_lpcg - > num_max ;
clks = clk_data - > hws ;
for ( i = 0 ; i < ss_lpcg - > num_lpcg ; i + + ) {
lpcg = ss_lpcg - > lpcg + i ;
clks [ lpcg - > id ] = imx_clk_lpcg_scu ( lpcg - > name , lpcg - > parent ,
lpcg - > flags , base + lpcg - > offset ,
lpcg - > bit_idx , lpcg - > hw_gate ) ;
}
for ( i = 0 ; i < clk_data - > num ; i + + ) {
if ( IS_ERR ( clks [ i ] ) )
pr_warn ( " i.MX clk %u: register failed with %ld \n " ,
i , PTR_ERR ( clks [ i ] ) ) ;
}
return of_clk_add_hw_provider ( np , of_clk_hw_onecell_get , clk_data ) ;
}
static const struct of_device_id imx8qxp_lpcg_match [ ] = {
{ . compatible = " fsl,imx8qxp-lpcg-adma " , & imx8qxp_ss_adma , } ,
{ . compatible = " fsl,imx8qxp-lpcg-conn " , & imx8qxp_ss_conn , } ,
{ . compatible = " fsl,imx8qxp-lpcg-lsio " , & imx8qxp_ss_lsio , } ,
{ /* sentinel */ }
} ;
static struct platform_driver imx8qxp_lpcg_clk_driver = {
. driver = {
. name = " imx8qxp-lpcg-clk " ,
. of_match_table = imx8qxp_lpcg_match ,
. suppress_bind_attrs = true ,
} ,
. probe = imx8qxp_lpcg_clk_probe ,
} ;
builtin_platform_driver ( imx8qxp_lpcg_clk_driver ) ;
2020-07-30 09:22:54 +08:00
MODULE_AUTHOR ( " Aisheng Dong <aisheng.dong@nxp.com> " ) ;
MODULE_DESCRIPTION ( " NXP i.MX8QXP LPCG clock driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;