// SPDX-License-Identifier: GPL-2.0/* * ti-sysc.c - Texas Instruments sysc interconnect target driver * * TI SoCs have an interconnect target wrapper IP for many devices. The wrapper * IP manages clock gating, resets, and PM capabilities for the connected devices. * * Copyright (C) 2017-2024 Texas Instruments Incorporated - https://www.ti.com/ * * Many features are based on the earlier omap_hwmod arch code with thanks to all * the people who developed and debugged the code over the years: * * Copyright (C) 2009-2011 Nokia Corporation * Copyright (C) 2011-2021 Texas Instruments Incorporated - https://www.ti.com/ */ #include <linux/io.h>#include <linux/clk.h>#include <linux/clkdev.h>#include <linux/cpu_pm.h>#include <linux/delay.h>#include <linux/list.h>#include <linux/module.h>#include <linux/platform_device.h>#include <linux/pm_domain.h>#include <linux/pm_runtime.h>#include <linux/reset.h>#include <linux/of_address.h>#include <linux/of_platform.h>#include <linux/slab.h>#include <linux/sys_soc.h>#include <linux/timekeeping.h>#include <linux/iopoll.h> #include <linux/platform_data/ti-sysc.h> #include <dt-bindings/bus/ti-sysc.h> #define DIS_ISP BIT(2)#define DIS_IVA BIT(1)#define DIS_SGX BIT(0) #define SOC_FLAG(match, flag) { .machine = match, .data = (void *)(flag), } #define MAX_MODULE_SOFTRESET_WAIT 10000 enum sysc_soc { SOC_UNKNOWN, SOC_2420, SOC_2430, SOC_AM33, SOC_3430, SOC_AM35, SOC_3630, SOC_4430, SOC_4460, SOC_4470, SOC_5430, SOC_AM3, SOC_AM4, SOC_DRA7,}; struct sysc_address { unsigned long base; struct list_head node;}; struct sysc_module { struct sysc *ddata; struct list_head node;}; struct sysc_soc_info { unsigned long general_purpose:1; enum sysc_soc soc; struct mutex list_lock; /* disabled and restored modules list lock */ struct list_head disabled_modules; struct list_head restored_modules; struct notifier_block nb;}; enum sysc_clocks { SYSC_FCK, SYSC_ICK, SYSC_OPTFCK0, SYSC_OPTFCK1, SYSC_OPTFCK2, SYSC_OPTFCK3, SYSC_OPTFCK4, SYSC_OPTFCK5, SYSC_OPTFCK6, SYSC_OPTFCK7, SYSC_MAX_CLOCKS,}; static struct sysc_soc_info *sysc_soc;static const char * const reg_names[] = { "rev", "sysc", "syss", };static const char * const clock_names[SYSC_MAX_CLOCKS] = { "fck", "ick", "opt0", "opt1", "opt2", "opt3", "opt4", "opt5", "opt6", "opt7",}; #define SYSC_IDLEMODE_MASK 3#define SYSC_CLOCKACTIVITY_MASK 3 /** * struct sysc - TI sysc interconnect target module registers and capabilities * @dev: struct device pointer * @module_pa: physical address of the interconnect target module * @module_size: size of the interconnect target module * @module_va: virtual address of the interconnect target module * @offsets: register offsets from module base * @mdata: ti-sysc to hwmod translation data for a module * @clocks: clocks used by the interconnect target module * @clock_roles: clock role names for the found clocks * @nr_clocks: number of clocks used by the interconnect target module * @rsts: resets used by the interconnect target module * @legacy_mode: configured for legacy mode if set * @cap: interconnect target module capabilities * @cfg: interconnect target module configuration * @cookie: data used by legacy platform callbacks * @name: name if available * @revision: interconnect target module revision * @sysconfig: saved sysconfig register value * @reserved: target module is reserved and already in use * @enabled: sysc runtime enabled status * @needs_resume: runtime resume needed on resume from suspend * @child_needs_resume: runtime resume needed for child on resume from suspend * @idle_work: work structure used to perform delayed idle on a module * @pre_reset_quirk: module specific pre-reset quirk * @post_reset_quirk: module specific post-reset quirk * @reset_done_quirk: module specific reset done quirk * @module_enable_quirk: module specific enable quirk * @module_disable_quirk: module specific disable quirk * @module_unlock_quirk: module specific sysconfig unlock quirk * @module_lock_quirk: module specific sysconfig lock quirk */struct sysc { struct device *dev; u64 module_pa; u32 module_size; void __iomem *module_va; int offsets[SYSC_MAX_REGS]; struct ti_sysc_module_data *mdata; struct clk **clocks; const char **clock_roles; int nr_clocks; struct reset_control *rsts; const char *legacy_mode; const struct sysc_capabilities *cap; struct sysc_config cfg; struct ti_sysc_cookie cookie; const char *name; u32 revision; u32 sysconfig; unsigned int reserved:1; unsigned int enabled:1; unsigned int needs_resume:1; unsigned int child_needs_resume:1; struct delayed_work idle_work; void (*pre_reset_quirk)(struct sysc *sysc); void (*post_reset_quirk)(struct sysc *sysc); void (*reset_done_quirk)(struct sysc *sysc); void (*module_enable_quirk)(struct sysc *sysc); void (*module_disable_quirk)(struct sysc *sysc); void (*module_unlock_quirk)(struct sysc *sysc); void (*module_lock_quirk)(struct sysc *sysc);}; static void sysc_parse_dts_quirks(struct sysc *ddata, struct device_node *np, bool is_child);static int sysc_reset(struct sysc *ddata); static void sysc_write(struct sysc *ddata, int offset, u32 value){ if (ddata->cfg.quirks & SYSC_QUIRK_16BIT) { writew_relaxed(value & 0xffff, ddata->module_va + offset); /* Only i2c revision has LO and HI register with stride of 4 */ if (ddata->offsets[SYSC_REVISION] >= 0 && offset == ddata->offsets[SYSC_REVISION]) { u16 hi = value >> 16; writew_relaxed(hi, ddata->module_va + offset + 4); } return; } writel_relaxed(value, ddata->module_va + offset);} static u32 sysc_read(struct sysc *ddata, int offset){ if (ddata->cfg.quirks & SYSC_QUIRK_16BIT) { u32 val; val = readw_relaxed(ddata->module_va + offset); /* Only i2c revision has LO and HI register with stride of 4 */ if (ddata->offsets[SYSC_REVISION] >= 0 && offset == ddata->offsets[SYSC_REVISION]) { u16 tmp = readw_relaxed(ddata->module_va + offset + 4); val |= tmp << 16; } return val; } return readl_relaxed(ddata->module_va + offset);} static bool sysc_opt_clks_needed(struct sysc *ddata){ return !!(ddata->cfg.quirks & SYSC_QUIRK_OPT_CLKS_NEEDED);} static u32 sysc_read_revision(struct sysc *ddata){ int offset = ddata->offsets[SYSC_REVISION]; if (offset < 0) return 0; return sysc_read(ddata, offset);} static u32 sysc_read_sysconfig(struct sysc *ddata){ int offset = ddata->offsets[SYSC_SYSCONFIG]; if (offset < 0) return 0; return sysc_read(ddata, offset);} static u32 sysc_read_sysstatus(struct sysc *ddata){ int offset = ddata->offsets[SYSC_SYSSTATUS]; if (offset < 0) return 0; return sysc_read(ddata, offset);} static int sysc_poll_reset_sysstatus(struct sysc *ddata){ int error, retries; u32 syss_done, rstval; if (ddata->cfg.quirks & SYSS_QUIRK_RESETDONE_INVERTED) syss_done = 0; else syss_done = ddata->cfg.syss_mask; if (likely(!timekeeping_suspended)) { error = readx_poll_timeout_atomic(sysc_read_sysstatus, ddata, rstval, (rstval & ddata->cfg.syss_mask) == syss_done, 100, MAX_MODULE_SOFTRESET_WAIT); } else { retries = MAX_MODULE_SOFTRESET_WAIT; while (retries--) { rstval = sysc_read_sysstatus(ddata); if ((rstval & ddata->cfg.syss_mask) == syss_done) return 0; udelay(2); /* Account for udelay flakeyness */ } error = -ETIMEDOUT; } return error;} static int sysc_poll_reset_sysconfig(struct sysc *ddata){ int error, retries; u32 sysc_mask, rstval; sysc_mask = BIT(ddata->cap->regbits->srst_shift); if (likely(!timekeeping_suspended)) { error = readx_poll_timeout_atomic(sysc_read_sysconfig, ddata, rstval, !(rstval & sysc_mask), 100, MAX_MODULE_SOFTRESET_WAIT); } else { retries = MAX_MODULE_SOFTRESET_WAIT; while (retries--) { rstval = sysc_read_sysconfig(ddata); if (!(rstval & sysc_mask)) return 0; udelay(2); /* Account for udelay flakeyness */ } error = -ETIMEDOUT; } return error;} /* Poll on reset status */static int sysc_wait_softreset(struct sysc *ddata){ int syss_offset, error = 0; if (ddata->cap->regbits->srst_shift < 0) return 0; syss_offset = ddata->offsets[SYSC_SYSSTATUS]; if (syss_offset >= 0) error = sysc_poll_reset_sysstatus(ddata); else if (ddata->cfg.quirks & SYSC_QUIRK_RESET_STATUS) error = sysc_poll_reset_sysconfig(ddata); return error;} static int sysc_add_named_clock_from_child(struct sysc *ddata, const char *name, const char *optfck_name){ struct device_node *np = ddata->dev->of_node; struct device_node *child; struct clk_lookup *cl; struct clk *clock; const char *n; if (name) n = name; else n = optfck_name; /* Does the clock alias already exist? */ clock = of_clk_get_by_name(np, n); if (!IS_ERR(clock)) { clk_put(clock); return 0; } child = of_get_next_available_child(np, NULL); if (!child) return -ENODEV; clock = devm_get_clk_from_child(ddata->dev, child, name); if (IS_ERR(clock)) return PTR_ERR(clock); /* * Use clkdev_add() instead of clkdev_alloc() to avoid the MAX_DEV_ID * limit for clk_get(). If cl ever needs to be freed, it should be done * with clkdev_drop(). */ cl = kzalloc_obj(*cl); if (!cl) return -ENOMEM; cl->con_id = n; cl->dev_id = dev_name(ddata->dev); cl->clk = clock; clkdev_add(cl); clk_put(clock); return 0;} static int sysc_init_ext_opt_clock(struct sysc *ddata, const char *name){ const char *optfck_name; int error, index; if (ddata->nr_clocks < SYSC_OPTFCK0) index = SYSC_OPTFCK0; else index = ddata->nr_clocks; if (name) optfck_name = name; else optfck_name = clock_names[index]; error = sysc_add_named_clock_from_child(ddata, name, optfck_name); if (error) return error; ddata->clock_roles[index] = optfck_name; ddata->nr_clocks++; return 0;} static int sysc_get_one_clock(struct sysc *ddata, const char *name){ int error, i, index = -ENODEV; if (!strncmp(clock_names[SYSC_FCK], name, 3)) index = SYSC_FCK; else if (!strncmp(clock_names[SYSC_ICK], name, 3)) index = SYSC_ICK; if (index < 0) { for (i = SYSC_OPTFCK0; i < SYSC_MAX_CLOCKS; i++) { if (!ddata->clocks[i]) { index = i; break; } } } if (index < 0) { dev_err(ddata->dev, "clock %s not added\n", name); return index; } ddata->clocks[index] = devm_clk_get(ddata->dev, name); if (IS_ERR(ddata->clocks[index])) { dev_err(ddata->dev, "clock get error for %s: %li\n", name, PTR_ERR(ddata->clocks[index])); return PTR_ERR(ddata->clocks[index]); } error = clk_prepare(ddata->clocks[index]); if (error) { dev_err(ddata->dev, "clock prepare error for %s: %i\n", name, error); return error; } return 0;} static int sysc_get_clocks(struct sysc *ddata){ struct device_node *np = ddata->dev->of_node; struct property *prop; const char *name; int nr_fck = 0, nr_ick = 0, i, error = 0; ddata->clock_roles = devm_kcalloc(ddata->dev, SYSC_MAX_CLOCKS, sizeof(*ddata->clock_roles), GFP_KERNEL); if (!ddata->clock_roles) return -ENOMEM; of_property_for_each_string(np, "clock-names", prop, name) { if (!strncmp(clock_names[SYSC_FCK], name, 3)) nr_fck++; if (!strncmp(clock_names[SYSC_ICK], name, 3)) nr_ick++; ddata->clock_roles[ddata->nr_clocks] = name; ddata->nr_clocks++; } if (ddata->nr_clocks < 1) return 0; if ((ddata->cfg.quirks & SYSC_QUIRK_EXT_OPT_CLOCK)) { error = sysc_init_ext_opt_clock(ddata, NULL); if (error) return error; } if (ddata->nr_clocks > SYSC_MAX_CLOCKS) { dev_err(ddata->dev, "too many clocks for %pOF\n", np); return -EINVAL; } if (nr_fck > 1 || nr_ick > 1) { dev_err(ddata->dev, "max one fck and ick for %pOF\n", np); return -EINVAL; } /* Always add a slot for main clocks fck and ick even if unused */ if (!nr_fck) ddata->nr_clocks++; if (!nr_ick) ddata->nr_clocks++; ddata->clocks = devm_kcalloc(ddata->dev, ddata->nr_clocks, sizeof(*ddata->clocks), GFP_KERNEL); if (!ddata->clocks) return -ENOMEM; for (i = 0; i < SYSC_MAX_CLOCKS; i++) { const char *name = ddata->clock_roles[i]; if (!name) continue; error = sysc_get_one_clock(ddata, name);Lines 1–500 of 3351. Load more
// SPDX-License-Identifier: GPL-2.0 /* * ti-sysc.c - Texas Instruments sysc interconnect target driver * * TI SoCs have an interconnect target wrapper IP for many devices. The wrapper * IP manages clock gating, resets, and PM capabilities for the connected devices. * * Copyright (C) 2017-2024 Texas Instruments Incorporated - https://www.ti.com/ * * Many features are based on the earlier omap_hwmod arch code with thanks to all * the people who developed and debugged the code over the years: * * Copyright (C) 2009-2011 Nokia Corporation * Copyright (C) 2011-2021 Texas Instruments Incorporated - https://www.ti.com/ */ #include <linux/io.h> #include <linux/clk.h> #include <linux/clkdev.h> #include <linux/cpu_pm.h> #include <linux/delay.h> #include <linux/list.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/pm_domain.h> #include <linux/pm_runtime.h> #include <linux/reset.h> #include <linux/of_address.h> #include <linux/of_platform.h> #include <linux/slab.h> #include <linux/sys_soc.h> #include <linux/timekeeping.h> #include <linux/iopoll.h> #include <linux/platform_data/ti-sysc.h> #include <dt-bindings/bus/ti-sysc.h> #define DIS_ISP BIT(2) #define DIS_IVA BIT(1) #define DIS_SGX BIT(0) #define SOC_FLAG(match, flag) { .machine = match, .data = (void *)(flag), } #define MAX_MODULE_SOFTRESET_WAIT 10000 enum sysc_soc { SOC_UNKNOWN, SOC_2420, SOC_2430, SOC_AM33, SOC_3430, SOC_AM35, SOC_3630, SOC_4430, SOC_4460, SOC_4470, SOC_5430, SOC_AM3, SOC_AM4, SOC_DRA7, }; struct sysc_address { unsigned long base; struct list_head node; }; struct sysc_module { struct sysc *ddata; struct list_head node; }; struct sysc_soc_info { unsigned long general_purpose:1; enum sysc_soc soc; struct mutex list_lock; /* disabled and restored modules list lock */ struct list_head disabled_modules; struct list_head restored_modules; struct notifier_block nb; }; enum sysc_clocks { SYSC_FCK, SYSC_ICK, SYSC_OPTFCK0, SYSC_OPTFCK1, SYSC_OPTFCK2, SYSC_OPTFCK3, SYSC_OPTFCK4, SYSC_OPTFCK5, SYSC_OPTFCK6, SYSC_OPTFCK7, SYSC_MAX_CLOCKS, }; static struct sysc_soc_info *sysc_soc; static const char * const reg_names[] = { "rev", "sysc", "syss", }; static const char * const clock_names[SYSC_MAX_CLOCKS] = { "fck", "ick", "opt0", "opt1", "opt2", "opt3", "opt4", "opt5", "opt6", "opt7", }; #define SYSC_IDLEMODE_MASK 3 #define SYSC_CLOCKACTIVITY_MASK 3 /** * struct sysc - TI sysc interconnect target module registers and capabilities * @dev: struct device pointer * @module_pa: physical address of the interconnect target module * @module_size: size of the interconnect target module * @module_va: virtual address of the interconnect target module * @offsets: register offsets from module base * @mdata: ti-sysc to hwmod translation data for a module * @clocks: clocks used by the interconnect target module * @clock_roles: clock role names for the found clocks * @nr_clocks: number of clocks used by the interconnect target module * @rsts: resets used by the interconnect target module * @legacy_mode: configured for legacy mode if set * @cap: interconnect target module capabilities * @cfg: interconnect target module configuration * @cookie: data used by legacy platform callbacks * @name: name if available * @revision: interconnect target module revision * @sysconfig: saved sysconfig register value * @reserved: target module is reserved and already in use * @enabled: sysc runtime enabled status * @needs_resume: runtime resume needed on resume from suspend * @child_needs_resume: runtime resume needed for child on resume from suspend * @idle_work: work structure used to perform delayed idle on a module * @pre_reset_quirk: module specific pre-reset quirk * @post_reset_quirk: module specific post-reset quirk * @reset_done_quirk: module specific reset done quirk * @module_enable_quirk: module specific enable quirk * @module_disable_quirk: module specific disable quirk * @module_unlock_quirk: module specific sysconfig unlock quirk * @module_lock_quirk: module specific sysconfig lock quirk */ struct sysc { struct device *dev; u64 module_pa; u32 module_size; void __iomem *module_va; int offsets[SYSC_MAX_REGS]; struct ti_sysc_module_data *mdata; struct clk **clocks; const char **clock_roles; int nr_clocks; struct reset_control *rsts; const char *legacy_mode; const struct sysc_capabilities *cap; struct sysc_config cfg; struct ti_sysc_cookie cookie; const char *name; u32 revision; u32 sysconfig; unsigned int reserved:1; unsigned int enabled:1; unsigned int needs_resume:1; unsigned int child_needs_resume:1; struct delayed_work idle_work; void (*pre_reset_quirk)(struct sysc *sysc); void (*post_reset_quirk)(struct sysc *sysc); void (*reset_done_quirk)(struct sysc *sysc); void (*module_enable_quirk)(struct sysc *sysc); void (*module_disable_quirk)(struct sysc *sysc); void (*module_unlock_quirk)(struct sysc *sysc); void (*module_lock_quirk)(struct sysc *sysc); }; static void sysc_parse_dts_quirks(struct sysc *ddata, struct device_node *np, bool is_child); static int sysc_reset(struct sysc *ddata); static void sysc_write(struct sysc *ddata, int offset, u32 value) { if (ddata->cfg.quirks & SYSC_QUIRK_16BIT) { writew_relaxed(value & 0xffff, ddata->module_va + offset); /* Only i2c revision has LO and HI register with stride of 4 */ if (ddata->offsets[SYSC_REVISION] >= 0 && offset == ddata->offsets[SYSC_REVISION]) { u16 hi = value >> 16; writew_relaxed(hi, ddata->module_va + offset + 4); } return; } writel_relaxed(value, ddata->module_va + offset); } static u32 sysc_read(struct sysc *ddata, int offset) { if (ddata->cfg.quirks & SYSC_QUIRK_16BIT) { u32 val; val = readw_relaxed(ddata->module_va + offset); /* Only i2c revision has LO and HI register with stride of 4 */ if (ddata->offsets[SYSC_REVISION] >= 0 && offset == ddata->offsets[SYSC_REVISION]) { u16 tmp = readw_relaxed(ddata->module_va + offset + 4); val |= tmp << 16; } return val; } return readl_relaxed(ddata->module_va + offset); } static bool sysc_opt_clks_needed(struct sysc *ddata) { return !!(ddata->cfg.quirks & SYSC_QUIRK_OPT_CLKS_NEEDED); } static u32 sysc_read_revision(struct sysc *ddata) { int offset = ddata->offsets[SYSC_REVISION]; if (offset < 0) return 0; return sysc_read(ddata, offset); } static u32 sysc_read_sysconfig(struct sysc *ddata) { int offset = ddata->offsets[SYSC_SYSCONFIG]; if (offset < 0) return 0; return sysc_read(ddata, offset); } static u32 sysc_read_sysstatus(struct sysc *ddata) { int offset = ddata->offsets[SYSC_SYSSTATUS]; if (offset < 0) return 0; return sysc_read(ddata, offset); } static int sysc_poll_reset_sysstatus(struct sysc *ddata) { int error, retries; u32 syss_done, rstval; if (ddata->cfg.quirks & SYSS_QUIRK_RESETDONE_INVERTED) syss_done = 0; else syss_done = ddata->cfg.syss_mask; if (likely(!timekeeping_suspended)) { error = readx_poll_timeout_atomic(sysc_read_sysstatus, ddata, rstval, (rstval & ddata->cfg.syss_mask) == syss_done, 100, MAX_MODULE_SOFTRESET_WAIT); } else { retries = MAX_MODULE_SOFTRESET_WAIT; while (retries--) { rstval = sysc_read_sysstatus(ddata); if ((rstval & ddata->cfg.syss_mask) == syss_done) return 0; udelay(2); /* Account for udelay flakeyness */ } error = -ETIMEDOUT; } return error; } static int sysc_poll_reset_sysconfig(struct sysc *ddata) { int error, retries; u32 sysc_mask, rstval; sysc_mask = BIT(ddata->cap->regbits->srst_shift); if (likely(!timekeeping_suspended)) { error = readx_poll_timeout_atomic(sysc_read_sysconfig, ddata, rstval, !(rstval & sysc_mask), 100, MAX_MODULE_SOFTRESET_WAIT); } else { retries = MAX_MODULE_SOFTRESET_WAIT; while (retries--) { rstval = sysc_read_sysconfig(ddata); if (!(rstval & sysc_mask)) return 0; udelay(2); /* Account for udelay flakeyness */ } error = -ETIMEDOUT; } return error; } /* Poll on reset status */ static int sysc_wait_softreset(struct sysc *ddata) { int syss_offset, error = 0; if (ddata->cap->regbits->srst_shift < 0) return 0; syss_offset = ddata->offsets[SYSC_SYSSTATUS]; if (syss_offset >= 0) error = sysc_poll_reset_sysstatus(ddata); else if (ddata->cfg.quirks & SYSC_QUIRK_RESET_STATUS) error = sysc_poll_reset_sysconfig(ddata); return error; } static int sysc_add_named_clock_from_child(struct sysc *ddata, const char *name, const char *optfck_name) { struct device_node *np = ddata->dev->of_node; struct device_node *child; struct clk_lookup *cl; struct clk *clock; const char *n; if (name) n = name; else n = optfck_name; /* Does the clock alias already exist? */ clock = of_clk_get_by_name(np, n); if (!IS_ERR(clock)) { clk_put(clock); return 0; } child = of_get_next_available_child(np, NULL); if (!child) return -ENODEV; clock = devm_get_clk_from_child(ddata->dev, child, name); if (IS_ERR(clock)) return PTR_ERR(clock); /* * Use clkdev_add() instead of clkdev_alloc() to avoid the MAX_DEV_ID * limit for clk_get(). If cl ever needs to be freed, it should be done * with clkdev_drop(). */ cl = kzalloc_obj(*cl); if (!cl) return -ENOMEM; cl->con_id = n; cl->dev_id = dev_name(ddata->dev); cl->clk = clock; clkdev_add(cl); clk_put(clock); return 0; } static int sysc_init_ext_opt_clock(struct sysc *ddata, const char *name) { const char *optfck_name; int error, index; if (ddata->nr_clocks < SYSC_OPTFCK0) index = SYSC_OPTFCK0; else index = ddata->nr_clocks; if (name) optfck_name = name; else optfck_name = clock_names[index]; error = sysc_add_named_clock_from_child(ddata, name, optfck_name); if (error) return error; ddata->clock_roles[index] = optfck_name; ddata->nr_clocks++; return 0; } static int sysc_get_one_clock(struct sysc *ddata, const char *name) { int error, i, index = -ENODEV; if (!strncmp(clock_names[SYSC_FCK], name, 3)) index = SYSC_FCK; else if (!strncmp(clock_names[SYSC_ICK], name, 3)) index = SYSC_ICK; if (index < 0) { for (i = SYSC_OPTFCK0; i < SYSC_MAX_CLOCKS; i++) { if (!ddata->clocks[i]) { index = i; break; } } } if (index < 0) { dev_err(ddata->dev, "clock %s not added\n", name); return index; } ddata->clocks[index] = devm_clk_get(ddata->dev, name); if (IS_ERR(ddata->clocks[index])) { dev_err(ddata->dev, "clock get error for %s: %li\n", name, PTR_ERR(ddata->clocks[index])); return PTR_ERR(ddata->clocks[index]); } error = clk_prepare(ddata->clocks[index]); if (error) { dev_err(ddata->dev, "clock prepare error for %s: %i\n", name, error); return error; } return 0; } static int sysc_get_clocks(struct sysc *ddata) { struct device_node *np = ddata->dev->of_node; struct property *prop; const char *name; int nr_fck = 0, nr_ick = 0, i, error = 0; ddata->clock_roles = devm_kcalloc(ddata->dev, SYSC_MAX_CLOCKS, sizeof(*ddata->clock_roles), GFP_KERNEL); if (!ddata->clock_roles) return -ENOMEM; of_property_for_each_string(np, "clock-names", prop, name) { if (!strncmp(clock_names[SYSC_FCK], name, 3)) nr_fck++; if (!strncmp(clock_names[SYSC_ICK], name, 3)) nr_ick++; ddata->clock_roles[ddata->nr_clocks] = name; ddata->nr_clocks++; } if (ddata->nr_clocks < 1) return 0; if ((ddata->cfg.quirks & SYSC_QUIRK_EXT_OPT_CLOCK)) { error = sysc_init_ext_opt_clock(ddata, NULL); if (error) return error; } if (ddata->nr_clocks > SYSC_MAX_CLOCKS) { dev_err(ddata->dev, "too many clocks for %pOF\n", np); return -EINVAL; } if (nr_fck > 1 || nr_ick > 1) { dev_err(ddata->dev, "max one fck and ick for %pOF\n", np); return -EINVAL; } /* Always add a slot for main clocks fck and ick even if unused */ if (!nr_fck) ddata->nr_clocks++; if (!nr_ick) ddata->nr_clocks++; ddata->clocks = devm_kcalloc(ddata->dev, ddata->nr_clocks, sizeof(*ddata->clocks), GFP_KERNEL); if (!ddata->clocks) return -ENOMEM; for (i = 0; i < SYSC_MAX_CLOCKS; i++) { const char *name = ddata->clock_roles[i]; if (!name) continue; error = sysc_get_one_clock(ddata, name);