Changes in 4.19.323 staging: iio: frequency: ad9833: Get frequency value statically staging: iio: frequency: ad9833: Load clock using clock framework staging: iio: frequency: ad9834: Validate frequency parameter value usbnet: ipheth: fix carrier detection in modes 1 and 4 net: ethernet: use ip_hdrlen() instead of bit shift net: phy: vitesse: repair vsc73xx autonegotiation scripts: kconfig: merge_config: config files: add a trailing newline arm64: dts: rockchip: override BIOS_DISABLE signal via GPIO hog on RK3399 Puma net/mlx5: Update the list of the PCI supported devices net: ftgmac100: Enable TX interrupt to avoid TX timeout net: dpaa: Pad packets to ETH_ZLEN soundwire: stream: Revert "soundwire: stream: fix programming slave ports for non-continous port maps" selftests/vm: remove call to ksft_set_plan() selftests/kcmp: remove call to ksft_set_plan() ASoC: allow module autoloading for table db1200_pids pinctrl: at91: make it work with current gpiolib microblaze: don't treat zero reserved memory regions as error net: ftgmac100: Ensure tx descriptor updates are visible wifi: iwlwifi: mvm: fix iwl_mvm_max_scan_ie_fw_cmd_room() wifi: iwlwifi: mvm: don't wait for tx queues if firmware is dead ASoC: tda7419: fix module autoloading spi: bcm63xx: Enable module autoloading x86/hyperv: Set X86_FEATURE_TSC_KNOWN_FREQ when Hyper-V provides frequency ocfs2: add bounds checking to ocfs2_xattr_find_entry() ocfs2: strict bound check before memcmp in ocfs2_xattr_find_entry() gpio: prevent potential speculation leaks in gpio_device_get_desc() USB: serial: pl2303: add device id for Macrosilicon MS3020 ACPI: PMIC: Remove unneeded check in tps68470_pmic_opregion_probe() wifi: ath9k: fix parameter check in ath9k_init_debug() wifi: ath9k: Remove error checks when creating debugfs entries netfilter: nf_tables: elements with timeout below CONFIG_HZ never expire wifi: cfg80211: fix UBSAN noise in cfg80211_wext_siwscan() wifi: cfg80211: fix two more possible UBSAN-detected off-by-one errors wifi: mac80211: use two-phase skb reclamation in ieee80211_do_stop() can: bcm: Clear bo->bcm_proc_read after remove_proc_entry(). Bluetooth: btusb: Fix not handling ZPL/short-transfer block, bfq: fix possible UAF for bfqq->bic with merge chain block, bfq: choose the last bfqq from merge chain in bfq_setup_cooperator() block, bfq: don't break merge chain in bfq_split_bfqq() spi: ppc4xx: handle irq_of_parse_and_map() errors spi: ppc4xx: Avoid returning 0 when failed to parse and map IRQ ARM: versatile: fix OF node leak in CPUs prepare reset: berlin: fix OF node leak in probe() error path clocksource/drivers/qcom: Add missing iounmap() on errors in msm_dt_timer_init() hwmon: (max16065) Fix overflows seen when writing limits mtd: slram: insert break after errors in parsing the map hwmon: (ntc_thermistor) fix module autoloading power: supply: max17042_battery: Fix SOC threshold calc w/ no current sense fbdev: hpfb: Fix an error handling path in hpfb_dio_probe() drm/stm: Fix an error handling path in stm_drm_platform_probe() drm/amd: fix typo drm/amdgpu: Replace one-element array with flexible-array member drm/amdgpu: properly handle vbios fake edid sizing drm/radeon: Replace one-element array with flexible-array member drm/radeon: properly handle vbios fake edid sizing drm/rockchip: vop: Allow 4096px width scaling drm/radeon/evergreen_cs: fix int overflow errors in cs track offsets jfs: fix out-of-bounds in dbNextAG() and diAlloc() drm/msm/a5xx: properly clear preemption records on resume drm/msm/a5xx: fix races in preemption evaluation stage ipmi: docs: don't advertise deprecated sysfs entries drm/msm: fix %s null argument error xen: use correct end address of kernel for conflict checking xen/swiotlb: simplify range_straddles_page_boundary() xen/swiotlb: add alignment check for dma buffers selftests/bpf: Fix error compiling test_lru_map.c xz: cleanup CRC32 edits from 2018 kthread: add kthread_work tracepoints kthread: fix task state in kthread worker if being frozen jbd2: introduce/export functions jbd2_journal_submit|finish_inode_data_buffers() ext4: clear EXT4_GROUP_INFO_WAS_TRIMMED_BIT even mount with discard smackfs: Use rcu_assign_pointer() to ensure safe assignment in smk_set_cipso ext4: avoid negative min_clusters in find_group_orlov() ext4: return error on ext4_find_inline_entry ext4: avoid OOB when system.data xattr changes underneath the filesystem nilfs2: fix potential null-ptr-deref in nilfs_btree_insert() nilfs2: determine empty node blocks as corrupted nilfs2: fix potential oob read in nilfs_btree_check_delete() perf sched timehist: Fix missing free of session in perf_sched__timehist() perf sched timehist: Fixed timestamp error when unable to confirm event sched_in time perf time-utils: Fix 32-bit nsec parsing clk: rockchip: Set parent rate for DCLK_VOP clock on RK3228 drivers: media: dvb-frontends/rtl2832: fix an out-of-bounds write error drivers: media: dvb-frontends/rtl2830: fix an out-of-bounds write error PCI: xilinx-nwl: Fix register misspelling RDMA/iwcm: Fix WARNING:at_kernel/workqueue.c:#check_flush_dependency pinctrl: single: fix missing error code in pcs_probe() clk: ti: dra7-atl: Fix leak of of_nodes pinctrl: mvebu: Fix devinit_dove_pinctrl_probe function RDMA/cxgb4: Added NULL check for lookup_atid ntb: intel: Fix the NULL vs IS_ERR() bug for debugfs_create_dir() nfsd: call cache_put if xdr_reserve_space returns NULL f2fs: enhance to update i_mode and acl atomically in f2fs_setattr() f2fs: fix typo f2fs: fix to update i_ctime in __f2fs_setxattr() f2fs: remove unneeded check condition in __f2fs_setxattr() f2fs: reduce expensive checkpoint trigger frequency coresight: tmc: sg: Do not leak sg_table netfilter: nf_reject_ipv6: fix nf_reject_ip6_tcphdr_put() net: seeq: Fix use after free vulnerability in ether3 Driver Due to Race Condition tcp: introduce tcp_skb_timestamp_us() helper tcp: check skb is non-NULL in tcp_rto_delta_us() net: qrtr: Update packets cloning when broadcasting netfilter: ctnetlink: compile ctnetlink_label_size with CONFIG_NF_CONNTRACK_EVENTS crypto: aead,cipher - zeroize key buffer after use Remove *.orig pattern from .gitignore soc: versatile: integrator: fix OF node leak in probe() error path USB: appledisplay: close race between probe and completion handler USB: misc: cypress_cy7c63: check for short transfer firmware_loader: Block path traversal tty: rp2: Fix reset with non forgiving PCIe host bridges drbd: Fix atomicity violation in drbd_uuid_set_bm() drbd: Add NULL check for net_conf to prevent dereference in state validation ACPI: sysfs: validate return type of _STR method f2fs: prevent possible int overflow in dir_block_index() f2fs: avoid potential int overflow in sanity_check_area_boundary() vfs: fix race between evice_inodes() and find_inode()&iput() fs: Fix file_set_fowner LSM hook inconsistencies nfs: fix memory leak in error path of nfs4_do_reclaim PCI: xilinx-nwl: Use irq_data_get_irq_chip_data() PCI: xilinx-nwl: Fix off-by-one in INTx IRQ handler soc: versatile: realview: fix memory leak during device remove soc: versatile: realview: fix soc_dev leak during device remove usb: yurex: Replace snprintf() with the safer scnprintf() variant USB: misc: yurex: fix race between read and write pps: remove usage of the deprecated ida_simple_xx() API pps: add an error check in parport_attach i2c: aspeed: Update the stop sw state when the bus recovery occurs i2c: isch: Add missed 'else' usb: yurex: Fix inconsistent locking bug in yurex_read() mailbox: rockchip: fix a typo in module autoloading mailbox: bcm2835: Fix timeout during suspend mode ceph: remove the incorrect Fw reference check when dirtying pages netfilter: uapi: NFTA_FLOWTABLE_HOOK is NLA_NESTED netfilter: nf_tables: prevent nf_skb_duplicated corruption r8152: Factor out OOB link list waits net: ethernet: lantiq_etop: fix memory disclosure net: avoid potential underflow in qdisc_pkt_len_init() with UFO net: add more sanity checks to qdisc_pkt_len_init() ipv4: ip_gre: Fix drops of small packets in ipgre_xmit sctp: set sk_state back to CLOSED if autobind fails in sctp_listen_start ALSA: hda/generic: Unconditionally prefer preferred_dacs pairs ALSA: hda/conexant: Fix conflicting quirk for System76 Pangolin f2fs: Require FMODE_WRITE for atomic write ioctls wifi: ath9k: fix possible integer overflow in ath9k_get_et_stats() wifi: ath9k_htc: Use __skb_set_length() for resetting urb before resubmit net: hisilicon: hip04: fix OF node leak in probe() net: hisilicon: hns_dsaf_mac: fix OF node leak in hns_mac_get_info() net: hisilicon: hns_mdio: fix OF node leak in probe() ACPICA: Fix memory leak if acpi_ps_get_next_namepath() fails ACPICA: Fix memory leak if acpi_ps_get_next_field() fails ACPI: EC: Do not release locks during operation region accesses ACPICA: check null return of ACPI_ALLOCATE_ZEROED() in acpi_db_convert_to_package() tipc: guard against string buffer overrun net: mvpp2: Increase size of queue_name buffer ipv4: Check !in_dev earlier for ioctl(SIOCSIFADDR). ipv4: Mask upper DSCP bits and ECN bits in NETLINK_FIB_LOOKUP family tcp: avoid reusing FIN_WAIT2 when trying to find port in connect() process ACPICA: iasl: handle empty connection_node wifi: mwifiex: Fix memcpy() field-spanning write warning in mwifiex_cmd_802_11_scan_ext() signal: Replace BUG_ON()s ALSA: asihpi: Fix potential OOB array access ALSA: hdsp: Break infinite MIDI input flush loop fbdev: pxafb: Fix possible use after free in pxafb_task() power: reset: brcmstb: Do not go into infinite loop if reset fails ata: sata_sil: Rename sil_blacklist to sil_quirks jfs: UBSAN: shift-out-of-bounds in dbFindBits jfs: Fix uaf in dbFreeBits jfs: check if leafidx greater than num leaves per dmap tree jfs: Fix uninit-value access of new_ea in ea_buffer drm/amd/display: Check stream before comparing them drm/amd/display: Fix index out of bounds in degamma hardware format translation drm/printer: Allow NULL data in devcoredump printer scsi: aacraid: Rearrange order of struct aac_srb_unit drm/radeon/r100: Handle unknown family in r100_cp_init_microcode() of/irq: Refer to actual buffer size in of_irq_parse_one() ext4: ext4_search_dir should return a proper error ext4: fix i_data_sem unlock order in ext4_ind_migrate() spi: s3c64xx: fix timeout counters in flush_fifo selftests: breakpoints: use remaining time to check if suspend succeed selftests: vDSO: fix vDSO symbols lookup for powerpc64 i2c: xiic: Wait for TX empty to avoid missed TX NAKs spi: bcm63xx: Fix module autoloading perf/core: Fix small negative period being ignored parisc: Fix itlb miss handler for 64-bit programs ALSA: core: add isascii() check to card ID generator ext4: no need to continue when the number of entries is 1 ext4: propagate errors from ext4_find_extent() in ext4_insert_range() ext4: fix incorrect tid assumption in __jbd2_log_wait_for_space() ext4: aovid use-after-free in ext4_ext_insert_extent() ext4: fix double brelse() the buffer of the extents path ext4: fix incorrect tid assumption in ext4_wait_for_tail_page_commit() parisc: Fix 64-bit userspace syscall path of/irq: Support #msi-cells=<0> in of_msi_get_domain jbd2: stop waiting for space when jbd2_cleanup_journal_tail() returns error ocfs2: fix the la space leak when unmounting an ocfs2 volume ocfs2: fix uninit-value in ocfs2_get_block() ocfs2: reserve space for inline xattr before attaching reflink tree ocfs2: cancel dqi_sync_work before freeing oinfo ocfs2: remove unreasonable unlock in ocfs2_read_blocks ocfs2: fix null-ptr-deref when journal load failed. ocfs2: fix possible null-ptr-deref in ocfs2_set_buffer_uptodate riscv: define ILLEGAL_POINTER_VALUE for 64bit aoe: fix the potential use-after-free problem in more places clk: rockchip: fix error for unknown clocks media: uapi/linux/cec.h: cec_msg_set_reply_to: zero flags media: venus: fix use after free bug in venus_remove due to race condition iio: magnetometer: ak8975: Fix reading for ak099xx sensors tomoyo: fallback to realpath if symlink's pathname does not exist Input: adp5589-keys - fix adp5589_gpio_get_value() btrfs: wait for fixup workers before stopping cleaner kthread during umount gpio: davinci: fix lazy disable ext4: avoid ext4_error()'s caused by ENOMEM in the truncate path ext4: fix slab-use-after-free in ext4_split_extent_at() ext4: update orig_path in ext4_find_extent() arm64: Add Cortex-715 CPU part definition arm64: cputype: Add Neoverse-N3 definitions arm64: errata: Expand speculative SSBS workaround once more uprobes: fix kernel info leak via "[uprobes]" vma nfsd: use ktime_get_seconds() for timestamps nfsd: fix delegation_blocked() to block correctly for at least 30 seconds rtc: at91sam9: drop platform_data support rtc: at91sam9: fix OF node leak in probe() error path ACPI: battery: Simplify battery hook locking ACPI: battery: Fix possible crash when unregistering a battery hook ext4: fix inode tree inconsistency caused by ENOMEM net: ethernet: cortina: Drop TSO support tracing: Remove precision vsnprintf() check from print event drm: Move drm_mode_setcrtc() local re-init to failure path drm/crtc: fix uninitialized variable use even harder virtio_console: fix misc probe bugs Input: synaptics-rmi4 - fix UAF of IRQ domain on driver removal bpf: Check percpu map value size first s390/facility: Disable compile time optimization for decompressor code s390/mm: Add cond_resched() to cmm_alloc/free_pages() ext4: nested locking for xattr inode s390/cpum_sf: Remove WARN_ON_ONCE statements ktest.pl: Avoid false positives with grub2 skip regex clk: bcm: bcm53573: fix OF node leak in init i2c: i801: Use a different adapter-name for IDF adapters PCI: Mark Creative Labs EMU20k2 INTx masking as broken media: videobuf2-core: clear memory related fields in __vb2_plane_dmabuf_put() usb: chipidea: udc: enable suspend interrupt after usb reset tools/iio: Add memory allocation failure check for trigger_name driver core: bus: Return -EIO instead of 0 when show/store invalid bus attribute fbdev: sisfb: Fix strbuf array overflow NFS: Remove print_overflow_msg() SUNRPC: Fix integer overflow in decode_rc_list() tcp: fix tcp_enter_recovery() to zero retrans_stamp when it's safe netfilter: br_netfilter: fix panic with metadata_dst skb Bluetooth: RFCOMM: FIX possible deadlock in rfcomm_sk_state_change gpio: aspeed: Add the flush write to ensure the write complete. clk: Add (devm_)clk_get_optional() functions clk: generalize devm_clk_get() a bit clk: Provide new devm_clk helpers for prepared and enabled clocks gpio: aspeed: Use devm_clk api to manage clock source igb: Do not bring the device up after non-fatal error net: ibm: emac: mal: fix wrong goto ppp: fix ppp_async_encode() illegal access net: ipv6: ensure we call ipv6_mc_down() at most once CDC-NCM: avoid overflow in sanity checking HID: plantronics: Workaround for an unexcepted opposite volume key Revert "usb: yurex: Replace snprintf() with the safer scnprintf() variant" usb: xhci: Fix problem with xhci resume from suspend usb: storage: ignore bogus device raised by JieLi BR21 USB sound chip net: Fix an unsafe loop on the list posix-clock: Fix missing timespec64 check in pc_clock_settime() arm64: probes: Remove broken LDR (literal) uprobe support arm64: probes: Fix simulate_ldr*_literal() PCI: Add function 0 DMA alias quirk for Glenfly Arise chip fat: fix uninitialized variable KVM: Fix a data race on last_boosted_vcpu in kvm_vcpu_on_spin() net: dsa: mv88e6xxx: Fix out-of-bound access s390/sclp_vt220: Convert newlines to CRLF instead of LFCR KVM: s390: Change virtual to physical address access in diag 0x258 handler x86/cpufeatures: Define X86_FEATURE_AMD_IBPB_RET drm/vmwgfx: Handle surface check failure correctly iio: dac: stm32-dac-core: add missing select REGMAP_MMIO in Kconfig iio: adc: ti-ads8688: add missing select IIO_(TRIGGERED_)BUFFER in Kconfig iio: hid-sensors: Fix an error handling path in _hid_sensor_set_report_latency() iio: light: opt3001: add missing full-scale range value Bluetooth: Remove debugfs directory on module init failure Bluetooth: btusb: Fix regression with fake CSR controllers 0a12:0001 xhci: Fix incorrect stream context type macro USB: serial: option: add support for Quectel EG916Q-GL USB: serial: option: add Telit FN920C04 MBIM compositions parport: Proper fix for array out-of-bounds access x86/apic: Always explicitly disarm TSC-deadline timer nilfs2: propagate directory read errors from nilfs_find_entry() clk: Fix pointer casting to prevent oops in devm_clk_release() clk: Fix slab-out-of-bounds error in devm_clk_release() RDMA/bnxt_re: Fix incorrect AVID type in WQE structure RDMA/cxgb4: Fix RDMA_CM_EVENT_UNREACHABLE error for iWARP RDMA/bnxt_re: Return more meaningful error drm/msm/dsi: fix 32-bit signed integer extension in pclk_rate calculation macsec: don't increment counters for an unrelated SA net: ethernet: aeroflex: fix potential memory leak in greth_start_xmit_gbit() net: systemport: fix potential memory leak in bcm_sysport_xmit() usb: typec: altmode should keep reference to parent Bluetooth: bnep: fix wild-memory-access in proto_unregister arm64:uprobe fix the uprobe SWBP_INSN in big-endian arm64: probes: Fix uprobes for big-endian kernels KVM: s390: gaccess: Refactor gpa and length calculation KVM: s390: gaccess: Refactor access address range check KVM: s390: gaccess: Cleanup access to guest pages KVM: s390: gaccess: Check if guest address is in memslot udf: fix uninit-value use in udf_get_fileshortad jfs: Fix sanity check in dbMount net/sun3_82586: fix potential memory leak in sun3_82586_send_packet() be2net: fix potential memory leak in be_xmit() net: usb: usbnet: fix name regression posix-clock: posix-clock: Fix unbalanced locking in pc_clock_settime() ALSA: hda/realtek: Update default depop procedure drm/amd: Guard against bad data for ATIF ACPI method ACPI: button: Add DMI quirk for Samsung Galaxy Book2 to fix initial lid detection issue nilfs2: fix kernel bug due to missing clearing of buffer delay flag hv_netvsc: Fix VF namespace also in synthetic NIC NETDEV_REGISTER event selinux: improve error checking in sel_write_load() arm64/uprobes: change the uprobe_opcode_t typedef to fix the sparse warning xfrm: validate new SA's prefixlen using SA family when sel.family is unset usb: dwc3: remove generic PHY calibrate() calls usb: dwc3: Add splitdisable quirk for Hisilicon Kirin Soc usb: dwc3: core: Stop processing of pending events if controller is halted cgroup: Fix potential overflow issue when checking max_depth wifi: mac80211: skip non-uploaded keys in ieee80211_iter_keys gtp: simplify error handling code in 'gtp_encap_enable()' gtp: allow -1 to be specified as file description from userspace net/sched: stop qdisc_tree_reduce_backlog on TC_H_ROOT bpf: Fix out-of-bounds write in trie_get_next_key() net: support ip generic csum processing in skb_csum_hwoffload_help net: skip offload for NETIF_F_IPV6_CSUM if ipv6 header contains extension netfilter: nft_payload: sanitize offset and length before calling skb_checksum() firmware: arm_sdei: Fix the input parameter of cpuhp_remove_state() net: amd: mvme147: Fix probe banner message misc: sgi-gru: Don't disable preemption in GRU driver usbip: tools: Fix detach_port() invalid port error path usb: phy: Fix API devm_usb_put_phy() can not release the phy xhci: Fix Link TRB DMA in command ring stopped completion event Revert "driver core: Fix uevent_show() vs driver detach race" wifi: mac80211: do not pass a stopped vif to the driver in .get_txpower wifi: ath10k: Fix memory leak in management tx wifi: iwlegacy: Clear stale interrupts before resuming device nilfs2: fix potential deadlock with newly created symlinks ocfs2: pass u64 to ocfs2_truncate_inline maybe overflow nilfs2: fix kernel bug due to missing clearing of checked flag mm: shmem: fix data-race in shmem_getattr() vt: prevent kernel-infoleak in con_font_get() Linux 4.19.323 Change-Id: I2348f834187153067ab46b3b48b8fe7da9cee1f1 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
645 lines
17 KiB
C
645 lines
17 KiB
C
/*
|
|
* Copyright (c) 2014 MundoReader S.L.
|
|
* Author: Heiko Stuebner <heiko@sntech.de>
|
|
*
|
|
* Copyright (c) 2016 Rockchip Electronics Co. Ltd.
|
|
* Author: Xing Zheng <zhengxing@rock-chips.com>
|
|
*
|
|
* based on
|
|
*
|
|
* samsung/clk.c
|
|
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
|
|
* Copyright (c) 2013 Linaro Ltd.
|
|
* Author: Thomas Abraham <thomas.ab@samsung.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#include <linux/slab.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/clk-provider.h>
|
|
#include <linux/mfd/syscon.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/reboot.h>
|
|
#include <linux/rational.h>
|
|
#include "clk.h"
|
|
|
|
/**
|
|
* Register a clock branch.
|
|
* Most clock branches have a form like
|
|
*
|
|
* src1 --|--\
|
|
* |M |--[GATE]-[DIV]-
|
|
* src2 --|--/
|
|
*
|
|
* sometimes without one of those components.
|
|
*/
|
|
static struct clk *rockchip_clk_register_branch(const char *name,
|
|
const char *const *parent_names, u8 num_parents,
|
|
void __iomem *base,
|
|
int muxdiv_offset, u8 mux_shift, u8 mux_width, u8 mux_flags,
|
|
u8 div_shift, u8 div_width, u8 div_flags,
|
|
struct clk_div_table *div_table, int gate_offset,
|
|
u8 gate_shift, u8 gate_flags, unsigned long flags,
|
|
spinlock_t *lock)
|
|
{
|
|
struct clk *clk;
|
|
struct clk_mux *mux = NULL;
|
|
struct clk_gate *gate = NULL;
|
|
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);
|
|
if (!mux)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
mux->reg = base + muxdiv_offset;
|
|
mux->shift = mux_shift;
|
|
mux->mask = BIT(mux_width) - 1;
|
|
mux->flags = mux_flags;
|
|
mux->lock = lock;
|
|
mux_ops = (mux_flags & CLK_MUX_READ_ONLY) ? &clk_mux_ro_ops
|
|
: &clk_mux_ops;
|
|
}
|
|
|
|
if (gate_offset >= 0) {
|
|
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
|
|
if (!gate) {
|
|
ret = -ENOMEM;
|
|
goto err_gate;
|
|
}
|
|
|
|
gate->flags = gate_flags;
|
|
gate->reg = base + gate_offset;
|
|
gate->bit_idx = gate_shift;
|
|
gate->lock = lock;
|
|
gate_ops = &clk_gate_ops;
|
|
}
|
|
|
|
if (div_width > 0) {
|
|
div = kzalloc(sizeof(*div), GFP_KERNEL);
|
|
if (!div) {
|
|
ret = -ENOMEM;
|
|
goto err_div;
|
|
}
|
|
|
|
div->flags = div_flags;
|
|
div->reg = base + muxdiv_offset;
|
|
div->shift = div_shift;
|
|
div->width = div_width;
|
|
div->lock = lock;
|
|
div->table = div_table;
|
|
div_ops = (div_flags & CLK_DIVIDER_READ_ONLY)
|
|
? &clk_divider_ro_ops
|
|
: &clk_divider_ops;
|
|
}
|
|
|
|
clk = clk_register_composite(NULL, name, parent_names, num_parents,
|
|
mux ? &mux->hw : NULL, mux_ops,
|
|
div ? &div->hw : NULL, div_ops,
|
|
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(ret);
|
|
}
|
|
|
|
struct rockchip_clk_frac {
|
|
struct notifier_block clk_nb;
|
|
struct clk_fractional_divider div;
|
|
struct clk_gate gate;
|
|
|
|
struct clk_mux mux;
|
|
const struct clk_ops *mux_ops;
|
|
int mux_frac_idx;
|
|
|
|
bool rate_change_remuxed;
|
|
int rate_change_idx;
|
|
};
|
|
|
|
#define to_rockchip_clk_frac_nb(nb) \
|
|
container_of(nb, struct rockchip_clk_frac, clk_nb)
|
|
|
|
static int rockchip_clk_frac_notifier_cb(struct notifier_block *nb,
|
|
unsigned long event, void *data)
|
|
{
|
|
struct clk_notifier_data *ndata = data;
|
|
struct rockchip_clk_frac *frac = to_rockchip_clk_frac_nb(nb);
|
|
struct clk_mux *frac_mux = &frac->mux;
|
|
int ret = 0;
|
|
|
|
pr_debug("%s: event %lu, old_rate %lu, new_rate: %lu\n",
|
|
__func__, event, ndata->old_rate, ndata->new_rate);
|
|
if (event == PRE_RATE_CHANGE) {
|
|
frac->rate_change_idx =
|
|
frac->mux_ops->get_parent(&frac_mux->hw);
|
|
if (frac->rate_change_idx != frac->mux_frac_idx) {
|
|
frac->mux_ops->set_parent(&frac_mux->hw,
|
|
frac->mux_frac_idx);
|
|
frac->rate_change_remuxed = 1;
|
|
}
|
|
} else if (event == POST_RATE_CHANGE) {
|
|
/*
|
|
* The POST_RATE_CHANGE notifier runs directly after the
|
|
* divider clock is set in clk_change_rate, so we'll have
|
|
* remuxed back to the original parent before clk_change_rate
|
|
* reaches the mux itself.
|
|
*/
|
|
if (frac->rate_change_remuxed) {
|
|
frac->mux_ops->set_parent(&frac_mux->hw,
|
|
frac->rate_change_idx);
|
|
frac->rate_change_remuxed = 0;
|
|
}
|
|
}
|
|
|
|
return notifier_from_errno(ret);
|
|
}
|
|
|
|
/**
|
|
* fractional divider must set that denominator is 20 times larger than
|
|
* numerator to generate precise clock frequency.
|
|
*/
|
|
static void rockchip_fractional_approximation(struct clk_hw *hw,
|
|
unsigned long rate, unsigned long *parent_rate,
|
|
unsigned long *m, unsigned long *n)
|
|
{
|
|
struct clk_fractional_divider *fd = to_clk_fd(hw);
|
|
unsigned long p_rate, p_parent_rate;
|
|
struct clk_hw *p_parent;
|
|
unsigned long scale;
|
|
|
|
p_rate = clk_hw_get_rate(clk_hw_get_parent(hw));
|
|
if ((rate * 20 > p_rate) && (p_rate % rate != 0)) {
|
|
p_parent = clk_hw_get_parent(clk_hw_get_parent(hw));
|
|
p_parent_rate = clk_hw_get_rate(p_parent);
|
|
*parent_rate = p_parent_rate;
|
|
}
|
|
|
|
/*
|
|
* Get rate closer to *parent_rate to guarantee there is no overflow
|
|
* for m and n. In the result it will be the nearest rate left shifted
|
|
* by (scale - fd->nwidth) bits.
|
|
*/
|
|
scale = fls_long(*parent_rate / rate - 1);
|
|
if (scale > fd->nwidth)
|
|
rate <<= scale - fd->nwidth;
|
|
|
|
rational_best_approximation(rate, *parent_rate,
|
|
GENMASK(fd->mwidth - 1, 0), GENMASK(fd->nwidth - 1, 0),
|
|
m, n);
|
|
}
|
|
|
|
static struct clk *rockchip_clk_register_frac_branch(
|
|
struct rockchip_clk_provider *ctx, const char *name,
|
|
const char *const *parent_names, u8 num_parents,
|
|
void __iomem *base, int muxdiv_offset, u8 div_flags,
|
|
int gate_offset, u8 gate_shift, u8 gate_flags,
|
|
unsigned long flags, struct rockchip_clk_branch *child,
|
|
spinlock_t *lock)
|
|
{
|
|
struct rockchip_clk_frac *frac;
|
|
struct clk *clk;
|
|
struct clk_gate *gate = NULL;
|
|
struct clk_fractional_divider *div = NULL;
|
|
const struct clk_ops *div_ops = NULL, *gate_ops = NULL;
|
|
|
|
if (muxdiv_offset < 0)
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
if (child && child->branch_type != branch_mux) {
|
|
pr_err("%s: fractional child clock for %s can only be a mux\n",
|
|
__func__, name);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
frac = kzalloc(sizeof(*frac), GFP_KERNEL);
|
|
if (!frac)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
if (gate_offset >= 0) {
|
|
gate = &frac->gate;
|
|
gate->flags = gate_flags;
|
|
gate->reg = base + gate_offset;
|
|
gate->bit_idx = gate_shift;
|
|
gate->lock = lock;
|
|
gate_ops = &clk_gate_ops;
|
|
}
|
|
|
|
div = &frac->div;
|
|
div->flags = div_flags;
|
|
div->reg = base + muxdiv_offset;
|
|
div->mshift = 16;
|
|
div->mwidth = 16;
|
|
div->mmask = GENMASK(div->mwidth - 1, 0) << div->mshift;
|
|
div->nshift = 0;
|
|
div->nwidth = 16;
|
|
div->nmask = GENMASK(div->nwidth - 1, 0) << div->nshift;
|
|
div->lock = lock;
|
|
div->approximation = rockchip_fractional_approximation;
|
|
div_ops = &clk_fractional_divider_ops;
|
|
|
|
clk = clk_register_composite(NULL, name, parent_names, num_parents,
|
|
NULL, NULL,
|
|
&div->hw, div_ops,
|
|
gate ? &gate->hw : NULL, gate_ops,
|
|
flags | CLK_SET_RATE_UNGATE);
|
|
if (IS_ERR(clk)) {
|
|
kfree(frac);
|
|
return clk;
|
|
}
|
|
|
|
if (child) {
|
|
struct clk_mux *frac_mux = &frac->mux;
|
|
struct clk_init_data init = {};
|
|
struct clk *mux_clk;
|
|
int ret;
|
|
|
|
frac->mux_frac_idx = match_string(child->parent_names,
|
|
child->num_parents, name);
|
|
frac->mux_ops = &clk_mux_ops;
|
|
frac->clk_nb.notifier_call = rockchip_clk_frac_notifier_cb;
|
|
|
|
frac_mux->reg = base + child->muxdiv_offset;
|
|
frac_mux->shift = child->mux_shift;
|
|
frac_mux->mask = BIT(child->mux_width) - 1;
|
|
frac_mux->flags = child->mux_flags;
|
|
frac_mux->lock = lock;
|
|
frac_mux->hw.init = &init;
|
|
|
|
init.name = child->name;
|
|
init.flags = child->flags | CLK_SET_RATE_PARENT;
|
|
init.ops = frac->mux_ops;
|
|
init.parent_names = child->parent_names;
|
|
init.num_parents = child->num_parents;
|
|
|
|
mux_clk = clk_register(NULL, &frac_mux->hw);
|
|
if (IS_ERR(mux_clk)) {
|
|
kfree(frac);
|
|
return clk;
|
|
}
|
|
|
|
rockchip_clk_add_lookup(ctx, mux_clk, child->id);
|
|
|
|
/* notifier on the fraction divider to catch rate changes */
|
|
if (frac->mux_frac_idx >= 0) {
|
|
pr_debug("%s: found fractional parent in mux at pos %d\n",
|
|
__func__, frac->mux_frac_idx);
|
|
ret = clk_notifier_register(clk, &frac->clk_nb);
|
|
if (ret)
|
|
pr_err("%s: failed to register clock notifier for %s\n",
|
|
__func__, name);
|
|
} else {
|
|
pr_warn("%s: could not find %s as parent of %s, rate changes may not work\n",
|
|
__func__, name, child->name);
|
|
}
|
|
}
|
|
|
|
return clk;
|
|
}
|
|
|
|
static struct clk *rockchip_clk_register_factor_branch(const char *name,
|
|
const char *const *parent_names, u8 num_parents,
|
|
void __iomem *base, unsigned int mult, unsigned int div,
|
|
int gate_offset, u8 gate_shift, u8 gate_flags,
|
|
unsigned long flags, spinlock_t *lock)
|
|
{
|
|
struct clk *clk;
|
|
struct clk_gate *gate = NULL;
|
|
struct clk_fixed_factor *fix = NULL;
|
|
|
|
/* without gate, register a simple factor clock */
|
|
if (gate_offset == 0) {
|
|
return clk_register_fixed_factor(NULL, name,
|
|
parent_names[0], flags, mult,
|
|
div);
|
|
}
|
|
|
|
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
|
|
if (!gate)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
gate->flags = gate_flags;
|
|
gate->reg = base + gate_offset;
|
|
gate->bit_idx = gate_shift;
|
|
gate->lock = lock;
|
|
|
|
fix = kzalloc(sizeof(*fix), GFP_KERNEL);
|
|
if (!fix) {
|
|
kfree(gate);
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
|
|
fix->mult = mult;
|
|
fix->div = div;
|
|
|
|
clk = clk_register_composite(NULL, name, parent_names, num_parents,
|
|
NULL, NULL,
|
|
&fix->hw, &clk_fixed_factor_ops,
|
|
&gate->hw, &clk_gate_ops, flags);
|
|
if (IS_ERR(clk)) {
|
|
kfree(fix);
|
|
kfree(gate);
|
|
}
|
|
|
|
return clk;
|
|
}
|
|
|
|
struct rockchip_clk_provider * __init rockchip_clk_init(struct device_node *np,
|
|
void __iomem *base, unsigned long nr_clks)
|
|
{
|
|
struct rockchip_clk_provider *ctx;
|
|
struct clk **clk_table;
|
|
int i;
|
|
|
|
ctx = kzalloc(sizeof(struct rockchip_clk_provider), GFP_KERNEL);
|
|
if (!ctx)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL);
|
|
if (!clk_table)
|
|
goto err_free;
|
|
|
|
for (i = 0; i < nr_clks; ++i)
|
|
clk_table[i] = ERR_PTR(-ENOENT);
|
|
|
|
ctx->reg_base = base;
|
|
ctx->clk_data.clks = clk_table;
|
|
ctx->clk_data.clk_num = nr_clks;
|
|
ctx->cru_node = np;
|
|
spin_lock_init(&ctx->lock);
|
|
|
|
ctx->grf = syscon_regmap_lookup_by_phandle(ctx->cru_node,
|
|
"rockchip,grf");
|
|
|
|
return ctx;
|
|
|
|
err_free:
|
|
kfree(ctx);
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
|
|
void __init rockchip_clk_of_add_provider(struct device_node *np,
|
|
struct rockchip_clk_provider *ctx)
|
|
{
|
|
if (of_clk_add_provider(np, of_clk_src_onecell_get,
|
|
&ctx->clk_data))
|
|
pr_err("%s: could not register clk provider\n", __func__);
|
|
}
|
|
|
|
void rockchip_clk_add_lookup(struct rockchip_clk_provider *ctx,
|
|
struct clk *clk, unsigned int id)
|
|
{
|
|
if (ctx->clk_data.clks && id)
|
|
ctx->clk_data.clks[id] = clk;
|
|
}
|
|
|
|
void __init rockchip_clk_register_plls(struct rockchip_clk_provider *ctx,
|
|
struct rockchip_pll_clock *list,
|
|
unsigned int nr_pll, int grf_lock_offset)
|
|
{
|
|
struct clk *clk;
|
|
int idx;
|
|
|
|
for (idx = 0; idx < nr_pll; idx++, list++) {
|
|
clk = rockchip_clk_register_pll(ctx, list->type, list->name,
|
|
list->parent_names, list->num_parents,
|
|
list->con_offset, grf_lock_offset,
|
|
list->lock_shift, list->mode_offset,
|
|
list->mode_shift, list->rate_table,
|
|
list->flags, list->pll_flags);
|
|
if (IS_ERR(clk)) {
|
|
pr_err("%s: failed to register clock %s\n", __func__,
|
|
list->name);
|
|
continue;
|
|
}
|
|
|
|
rockchip_clk_add_lookup(ctx, clk, list->id);
|
|
}
|
|
}
|
|
|
|
void __init rockchip_clk_register_branches(
|
|
struct rockchip_clk_provider *ctx,
|
|
struct rockchip_clk_branch *list,
|
|
unsigned int nr_clk)
|
|
{
|
|
struct clk *clk;
|
|
unsigned int idx;
|
|
unsigned long flags;
|
|
|
|
for (idx = 0; idx < nr_clk; idx++, list++) {
|
|
flags = list->flags;
|
|
clk = NULL;
|
|
|
|
/* catch simple muxes */
|
|
switch (list->branch_type) {
|
|
case branch_mux:
|
|
clk = clk_register_mux(NULL, list->name,
|
|
list->parent_names, list->num_parents,
|
|
flags, ctx->reg_base + list->muxdiv_offset,
|
|
list->mux_shift, list->mux_width,
|
|
list->mux_flags, &ctx->lock);
|
|
break;
|
|
case branch_muxgrf:
|
|
clk = rockchip_clk_register_muxgrf(list->name,
|
|
list->parent_names, list->num_parents,
|
|
flags, ctx->grf, list->muxdiv_offset,
|
|
list->mux_shift, list->mux_width,
|
|
list->mux_flags);
|
|
break;
|
|
case branch_divider:
|
|
if (list->div_table)
|
|
clk = clk_register_divider_table(NULL,
|
|
list->name, list->parent_names[0],
|
|
flags,
|
|
ctx->reg_base + list->muxdiv_offset,
|
|
list->div_shift, list->div_width,
|
|
list->div_flags, list->div_table,
|
|
&ctx->lock);
|
|
else
|
|
clk = clk_register_divider(NULL, list->name,
|
|
list->parent_names[0], flags,
|
|
ctx->reg_base + list->muxdiv_offset,
|
|
list->div_shift, list->div_width,
|
|
list->div_flags, &ctx->lock);
|
|
break;
|
|
case branch_fraction_divider:
|
|
clk = rockchip_clk_register_frac_branch(ctx, list->name,
|
|
list->parent_names, list->num_parents,
|
|
ctx->reg_base, list->muxdiv_offset,
|
|
list->div_flags,
|
|
list->gate_offset, list->gate_shift,
|
|
list->gate_flags, flags, list->child,
|
|
&ctx->lock);
|
|
break;
|
|
case branch_half_divider:
|
|
clk = rockchip_clk_register_halfdiv(list->name,
|
|
list->parent_names, list->num_parents,
|
|
ctx->reg_base, list->muxdiv_offset,
|
|
list->mux_shift, list->mux_width,
|
|
list->mux_flags, list->div_shift,
|
|
list->div_width, list->div_flags,
|
|
list->gate_offset, list->gate_shift,
|
|
list->gate_flags, flags, &ctx->lock);
|
|
break;
|
|
case branch_gate:
|
|
flags |= CLK_SET_RATE_PARENT;
|
|
|
|
clk = clk_register_gate(NULL, list->name,
|
|
list->parent_names[0], flags,
|
|
ctx->reg_base + list->gate_offset,
|
|
list->gate_shift, list->gate_flags, &ctx->lock);
|
|
break;
|
|
case branch_composite:
|
|
clk = rockchip_clk_register_branch(list->name,
|
|
list->parent_names, list->num_parents,
|
|
ctx->reg_base, list->muxdiv_offset,
|
|
list->mux_shift,
|
|
list->mux_width, list->mux_flags,
|
|
list->div_shift, list->div_width,
|
|
list->div_flags, list->div_table,
|
|
list->gate_offset, list->gate_shift,
|
|
list->gate_flags, flags, &ctx->lock);
|
|
break;
|
|
case branch_mmc:
|
|
clk = rockchip_clk_register_mmc(
|
|
list->name,
|
|
list->parent_names, list->num_parents,
|
|
ctx->reg_base + list->muxdiv_offset,
|
|
list->div_shift
|
|
);
|
|
break;
|
|
case branch_inverter:
|
|
clk = rockchip_clk_register_inverter(
|
|
list->name, list->parent_names,
|
|
list->num_parents,
|
|
ctx->reg_base + list->muxdiv_offset,
|
|
list->div_shift, list->div_flags, &ctx->lock);
|
|
break;
|
|
case branch_factor:
|
|
clk = rockchip_clk_register_factor_branch(
|
|
list->name, list->parent_names,
|
|
list->num_parents, ctx->reg_base,
|
|
list->div_shift, list->div_width,
|
|
list->gate_offset, list->gate_shift,
|
|
list->gate_flags, flags, &ctx->lock);
|
|
break;
|
|
case branch_ddrclk:
|
|
clk = rockchip_clk_register_ddrclk(
|
|
list->name, list->flags,
|
|
list->parent_names, list->num_parents,
|
|
list->muxdiv_offset, list->mux_shift,
|
|
list->mux_width, list->div_shift,
|
|
list->div_width, list->div_flags,
|
|
ctx->reg_base, &ctx->lock);
|
|
break;
|
|
}
|
|
|
|
/* none of the cases above matched */
|
|
if (!clk) {
|
|
pr_err("%s: unknown clock type %d\n",
|
|
__func__, list->branch_type);
|
|
continue;
|
|
}
|
|
|
|
if (IS_ERR(clk)) {
|
|
pr_err("%s: failed to register clock %s: %ld\n",
|
|
__func__, list->name, PTR_ERR(clk));
|
|
continue;
|
|
}
|
|
|
|
rockchip_clk_add_lookup(ctx, clk, list->id);
|
|
}
|
|
}
|
|
|
|
void __init rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx,
|
|
unsigned int lookup_id,
|
|
const char *name, const char *const *parent_names,
|
|
u8 num_parents,
|
|
const struct rockchip_cpuclk_reg_data *reg_data,
|
|
const struct rockchip_cpuclk_rate_table *rates,
|
|
int nrates)
|
|
{
|
|
struct clk *clk;
|
|
|
|
clk = rockchip_clk_register_cpuclk(name, parent_names, num_parents,
|
|
reg_data, rates, nrates,
|
|
ctx->reg_base, &ctx->lock);
|
|
if (IS_ERR(clk)) {
|
|
pr_err("%s: failed to register clock %s: %ld\n",
|
|
__func__, name, PTR_ERR(clk));
|
|
return;
|
|
}
|
|
|
|
rockchip_clk_add_lookup(ctx, clk, lookup_id);
|
|
}
|
|
|
|
void __init rockchip_clk_protect_critical(const char *const clocks[],
|
|
int nclocks)
|
|
{
|
|
int i;
|
|
|
|
/* Protect the clocks that needs to stay on */
|
|
for (i = 0; i < nclocks; i++) {
|
|
struct clk *clk = __clk_lookup(clocks[i]);
|
|
|
|
if (clk)
|
|
clk_prepare_enable(clk);
|
|
}
|
|
}
|
|
|
|
static void __iomem *rst_base;
|
|
static unsigned int reg_restart;
|
|
static void (*cb_restart)(void);
|
|
static int rockchip_restart_notify(struct notifier_block *this,
|
|
unsigned long mode, void *cmd)
|
|
{
|
|
if (cb_restart)
|
|
cb_restart();
|
|
|
|
writel(0xfdb9, rst_base + reg_restart);
|
|
return NOTIFY_DONE;
|
|
}
|
|
|
|
static struct notifier_block rockchip_restart_handler = {
|
|
.notifier_call = rockchip_restart_notify,
|
|
.priority = 128,
|
|
};
|
|
|
|
void __init
|
|
rockchip_register_restart_notifier(struct rockchip_clk_provider *ctx,
|
|
unsigned int reg,
|
|
void (*cb)(void))
|
|
{
|
|
int ret;
|
|
|
|
rst_base = ctx->reg_base;
|
|
reg_restart = reg;
|
|
cb_restart = cb;
|
|
ret = register_restart_handler(&rockchip_restart_handler);
|
|
if (ret)
|
|
pr_err("%s: cannot register restart handler, %d\n",
|
|
__func__, ret);
|
|
}
|