Changes in 4.9.311 USB: serial: pl2303: add IBM device IDs USB: serial: simple: add Nokia phone driver netdevice: add the case if dev is NULL virtio_console: break out of buf poll on remove ethernet: sun: Free the coherent when failing in probing af_key: add __GFP_ZERO flag for compose_sadb_supported in function pfkey_register block: Add a helper to validate the block size virtio-blk: Use blk_validate_block_size() to validate block size USB: usb-storage: Fix use of bitfields for hardware data in ene_ub6250.c coresight: Fix TRCCONFIGR.QE sysfs interface iio: inkern: apply consumer scale on IIO_VAL_INT cases iio: inkern: make a best effort on offset calculation clk: uniphier: Fix fixed-rate initialization ptrace: Check PTRACE_O_SUSPEND_SECCOMP permission on PTRACE_SEIZE SUNRPC: avoid race between mod_timer() and del_timer_sync() NFSD: prevent underflow in nfssvc_decode_writeargs() can: ems_usb: ems_usb_start_xmit(): fix double dev_kfree_skb() in error path jffs2: fix use-after-free in jffs2_clear_xattr_subsystem jffs2: fix memory leak in jffs2_do_mount_fs jffs2: fix memory leak in jffs2_scan_medium mm/pages_alloc.c: don't create ZONE_MOVABLE beyond the end of a node mempolicy: mbind_range() set_policy() after vma_merge() scsi: libsas: Fix sas_ata_qc_issue() handling of NCQ NON DATA commands Revert "Input: clear BTN_RIGHT/MIDDLE on buttonpads" ALSA: cs4236: fix an incorrect NULL check on list iterator drivers: hamradio: 6pack: fix UAF bug caused by mod_timer() video: fbdev: sm712fb: Fix crash in smtcfb_read() video: fbdev: atari: Atari 2 bpp (STe) palette bugfix ARM: dts: exynos: fix UART3 pins configuration in Exynos5250 ARM: dts: exynos: add missing HDMI supplies on SMDK5250 ARM: dts: exynos: add missing HDMI supplies on SMDK5420 carl9170: fix missing bit-wise or operator for tx_params thermal: int340x: Increase bitmap size lib/raid6/test: fix multiple definition linking error DEC: Limit PMAX memory probing to R3k systems media: davinci: vpif: fix unbalanced runtime PM get brcmfmac: firmware: Allocate space for default boardrev in nvram brcmfmac: pcie: Replace brcmf_pcie_copy_mem_todev with memcpy_toio PCI: pciehp: Clear cmd_busy bit in polling mode crypto: authenc - Fix sleep in atomic context in decrypt_tail crypto: mxs-dcp - Fix scatterlist processing spi: tegra114: Add missing IRQ check in tegra_spi_probe selftests/x86: Add validity check and allow field splitting hwmon: (pmbus) Add mutex to regulator ops hwmon: (sch56xx-common) Replace WDOG_ACTIVE with WDOG_HW_RUNNING PM: hibernate: fix __setup handler error handling PM: suspend: fix return value of __setup handler crypto: vmx - add missing dependencies crypto: ccp - ccp_dmaengine_unregister release dma channels hwmon: (pmbus) Add Vin unit off handling clocksource: acpi_pm: fix return value of __setup handler sched/debug: Remove mpol_get/put and task_lock/unlock from sched_show_numa perf/core: Fix address filter parser for multiple filters perf/x86/intel/pt: Fix address filter config for 32-bit kernel video: fbdev: smscufx: Fix null-ptr-deref in ufx_usb_probe() video: fbdev: fbcvt.c: fix printing in fb_cvt_print_name() ARM: dts: qcom: ipq4019: fix sleep clock soc: ti: wkup_m3_ipc: Fix IRQ check in wkup_m3_ipc_probe media: usb: go7007: s2250-board: fix leak in probe() ASoC: ti: davinci-i2s: Add check for clk_enable() ALSA: spi: Add check for clk_enable() arm64: dts: ns2: Fix spi-cpol and spi-cpha property arm64: dts: broadcom: Fix sata nodename printk: fix return value of printk.devkmsg __setup handler ASoC: mxs-saif: Handle errors for clk_enable ASoC: atmel_ssc_dai: Handle errors for clk_enable memory: emif: Add check for setup_interrupts memory: emif: check the pointer temp in get_device_details() ALSA: firewire-lib: fix uninitialized flag for AV/C deferred transaction ASoC: atmel: Add missing of_node_put() in at91sam9g20ek_audio_probe ASoC: wm8350: Handle error for wm8350_register_irq ASoC: fsi: Add check for clk_enable video: fbdev: omapfb: Add missing of_node_put() in dvic_probe_of ASoC: dmaengine: do not use a NULL prepare_slave_config() callback ASoC: mxs: Fix error handling in mxs_sgtl5000_probe ASoC: imx-es8328: Fix error return code in imx_es8328_probe() mtd: onenand: Check for error irq drm/edid: Don't clear formats if using deep color ath9k_htc: fix uninit value bugs ray_cs: Check ioremap return value power: supply: ab8500: Fix memory leak in ab8500_fg_sysfs_init HID: i2c-hid: fix GET/SET_REPORT for unnumbered reports iwlwifi: Fix -EIO error code that is never returned scsi: pm8001: Fix command initialization in pm80XX_send_read_log() scsi: pm8001: Fix command initialization in pm8001_chip_ssp_tm_req() scsi: pm8001: Fix payload initialization in pm80xx_set_thermal_config() scsi: pm8001: Fix abort all task initialization TOMOYO: fix __setup handlers return values ext2: correct max file size computing drm/tegra: Fix reference leak in tegra_dsi_ganged_probe KVM: x86: Fix emulation in writing cr8 KVM: x86/emulator: Defer not-present segment check in __load_segment_descriptor() i2c: xiic: Make bus names unique power: supply: wm8350-power: Handle error for wm8350_register_irq power: supply: wm8350-power: Add missing free in free_charger_irq powerpc/sysdev: fix incorrect use to determine if list is empty mfd: mc13xxx: Add check for mc13xxx_irq_request MIPS: RB532: fix return value of __setup handler USB: storage: ums-realtek: fix error code in rts51x_read_mem() af_netlink: Fix shift out of bounds in group mask calculation i2c: mux: demux-pinctrl: do not deactivate a master that is not active mfd: asic3: Add missing iounmap() on error asic3_mfd_probe mxser: fix xmit_buf leak in activate when LSR == 0xff pwm: lpc18xx-sct: Initialize driver data and hardware before pwmchip_add() iio: adc: Add check for devm_request_threaded_irq clk: qcom: clk-rcg2: Update the frac table for pixel clock remoteproc: qcom_wcnss: Add missing of_node_put() in wcnss_alloc_memory_region clk: loongson1: Terminate clk_div_table with sentinel element clk: clps711x: Terminate clk_div_table with sentinel element clk: tegra: tegra124-emc: Fix missing put_device() call in emc_ensure_emc_driver NFS: remove unneeded check in decode_devicenotify_args() pinctrl: mediatek: Fix missing of_node_put() in mtk_pctrl_init pinctrl: nomadik: Add missing of_node_put() in nmk_pinctrl_probe pinctrl/rockchip: Add missing of_node_put() in rockchip_pinctrl_probe tty: hvc: fix return value of __setup handler kgdboc: fix return value of __setup handler kgdbts: fix return value of __setup handler jfs: fix divide error in dbNextAG netfilter: nf_conntrack_tcp: preserve liberal flag in tcp options net: phy: broadcom: Fix brcm_fet_config_init() qlcnic: dcb: default to returning -EOPNOTSUPP net/x25: Fix null-ptr-deref caused by x25_disconnect selinux: use correct type for context length loop: use sysfs_emit() in the sysfs xxx show() Fix incorrect type in assignment of ipv6 port for audit irqchip/nvic: Release nvic_base upon failure ACPICA: Avoid walking the ACPI Namespace if it is not there ACPI/APEI: Limit printable size of BERT table data PM: core: keep irq flags in device_pm_check_callbacks() spi: tegra20: Use of_device_get_match_data() ext4: don't BUG if someone dirty pages without asking ext4 first ntfs: add sanity check on allocation size video: fbdev: nvidiafb: Use strscpy() to prevent buffer overflow video: fbdev: w100fb: Reset global state video: fbdev: cirrusfb: check pixclock to avoid divide by zero video: fbdev: omapfb: acx565akm: replace snprintf with sysfs_emit ARM: dts: qcom: fix gic_irq_domain_translate warnings for msm8960 ARM: dts: bcm2837: Add the missing L1/L2 cache information video: fbdev: omapfb: panel-dsi-cm: Use sysfs_emit() instead of snprintf() video: fbdev: omapfb: panel-tpo-td043mtea1: Use sysfs_emit() instead of snprintf() ASoC: soc-core: skip zero num_dai component in searching dai name media: cx88-mpeg: clear interrupt status register before streaming video ARM: tegra: tamonten: Fix I2C3 pad setting ARM: mmp: Fix failure to remove sram device video: fbdev: sm712fb: Fix crash in smtcfb_write() media: hdpvr: initialize dev->worker at hdpvr_register_videodev mmc: host: Return an error when ->enable_sdio_irq() ops is missing scsi: qla2xxx: Fix incorrect reporting of task management failure KVM: Prevent module exit until all VMs are freed ubifs: Add missing iput if do_tmpfile() failed in rename whiteout ubifs: setflags: Make dirtied_ino_d 8 bytes aligned gfs2: Make sure FITRIM minlen is rounded up to fs block size pinctrl: pinconf-generic: Print arguments for bias-pull-* ACPI: CPPC: Avoid out of bounds access when parsing _CPC data mm/mmap: return 1 from stack_guard_gap __setup() handler mm/memcontrol: return 1 from cgroup.memory __setup() handler ubi: fastmap: Return error code if memory allocation fails in add_aeb() ASoC: topology: Allow TLV control to be either read or write ARM: dts: spear1340: Update serial node properties ARM: dts: spear13xx: Update SPI dma properties openvswitch: Fixed nd target mask field in the flow dump. KVM: x86: Forbid VMM to set SYNIC/STIMER MSRs when SynIC wasn't activated rtc: wm8350: Handle error for wm8350_register_irq ARM: 9187/1: JIVE: fix return value of __setup handler KVM: x86/svm: Clear reserved bits written to PerfEvtSeln MSRs ath5k: fix OOB in ath5k_eeprom_read_pcal_info_5111 ptp: replace snprintf with sysfs_emit powerpc: dts: t104xrdb: fix phy type for FMAN 4/5 scsi: mvsas: Replace snprintf() with sysfs_emit() scsi: bfa: Replace snprintf() with sysfs_emit() iommu/arm-smmu-v3: fix event handling soft lockup dm ioctl: prevent potential spectre v1 gadget scsi: pm8001: Fix pm8001_mpi_task_abort_resp() scsi: aha152x: Fix aha152x_setup() __setup handler return value bnxt_en: Eliminate unintended link toggle during FW reset MIPS: fix fortify panic when copying asm exception handlers scsi: libfc: Fix use after free in fc_exch_abts_resp() usb: dwc3: omap: fix "unbalanced disables for smps10_out1" on omap5evm xtensa: fix DTC warning unit_address_format Bluetooth: Fix use after free in hci_send_acl init/main.c: return 1 from handled __setup() functions w1: w1_therm: fixes w1_seq for ds28ea00 sensors SUNRPC/call_alloc: async tasks mustn't block waiting for memory serial: samsung_tty: do not unlock port->lock for uart_write_wakeup() virtio_console: eliminate anonymous module_init & module_exit jfs: prevent NULL deref in diFree mm: fix race between MADV_FREE reclaim and blkdev direct IO read scsi: zorro7xx: Fix a resource leak in zorro7xx_remove_one() net: stmmac: Fix unset max_speed difference between DT and non-DT platforms drm/imx: Fix memory leak in imx_pd_connector_get_modes drbd: Fix five use after free bugs in get_initial_state mmmremap.c: avoid pointless invalidate_range_start/end on mremap(old_size=0) mm/mempolicy: fix mpol_new leak in shared_policy_replace x86/pm: Save the MSR validity status at context setup x86/speculation: Restore speculation related MSRs during S3 resume arm64: patch_text: Fixup last cpu should be master tools build: Use $(shell ) instead of `` to get embedded libperl's ccopts dmaengine: Revert "dmaengine: shdma: Fix runtime PM imbalance on error" mm: don't skip swap entry even if zap_details specified arm64: module: remove (NOLOAD) from linker script xfrm: policy: match with both mark and mask on user interfaces veth: Ensure eth header is in skb's linear part net: ethernet: stmmac: fix altr_tse_pcs function when using a fixed-link nfc: nci: add flush_workqueue to prevent uaf cifs: potential buffer overflow in handling symlinks drm/amdkfd: Check for potential null return of kmalloc_array() scsi: ibmvscsis: Increase INITIAL_SRP_LIMIT to 1024 net: micrel: fix KS8851_MLL Kconfig gpu: ipu-v3: Fix dev_dbg frequency output scsi: mvsas: Add PCI ID of RocketRaid 2640 drivers: net: slip: fix NPD bug in sl_tx_timeout() mm, page_alloc: fix build_zonerefs_node() mm: kmemleak: take a full lowmem check in kmemleak_*_phys() ALSA: pcm: Test for "silence" field in struct "pcm_format_data" ARM: davinci: da850-evm: Avoid NULL pointer dereference smp: Fix offline cpu check in flush_smp_call_function_queue() i2c: pasemi: Wait for write xfers to finish gcc-plugins: latent_entropy: use /dev/urandom Linux 4.9.311 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Change-Id: Ia8f55c5ae2f0eb71b0893d8271a10dfd3c78b3b8
819 lines
20 KiB
C
819 lines
20 KiB
C
/*
|
|
* Copyright (c) 2013, The Linux Foundation. All rights reserved.
|
|
*
|
|
* This software is licensed under the terms of the GNU General Public
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
* may be copied, distributed, and modified under those terms.
|
|
*
|
|
* 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/kernel.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/err.h>
|
|
#include <linux/bug.h>
|
|
#include <linux/export.h>
|
|
#include <linux/clk-provider.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/math64.h>
|
|
|
|
#include <asm/div64.h>
|
|
|
|
#include "clk-rcg.h"
|
|
#include "common.h"
|
|
|
|
#define CMD_REG 0x0
|
|
#define CMD_UPDATE BIT(0)
|
|
#define CMD_ROOT_EN BIT(1)
|
|
#define CMD_DIRTY_CFG BIT(4)
|
|
#define CMD_DIRTY_N BIT(5)
|
|
#define CMD_DIRTY_M BIT(6)
|
|
#define CMD_DIRTY_D BIT(7)
|
|
#define CMD_ROOT_OFF BIT(31)
|
|
|
|
#define CFG_REG 0x4
|
|
#define CFG_SRC_DIV_SHIFT 0
|
|
#define CFG_SRC_SEL_SHIFT 8
|
|
#define CFG_SRC_SEL_MASK (0x7 << CFG_SRC_SEL_SHIFT)
|
|
#define CFG_MODE_SHIFT 12
|
|
#define CFG_MODE_MASK (0x3 << CFG_MODE_SHIFT)
|
|
#define CFG_MODE_DUAL_EDGE (0x2 << CFG_MODE_SHIFT)
|
|
|
|
#define M_REG 0x8
|
|
#define N_REG 0xc
|
|
#define D_REG 0x10
|
|
|
|
static int clk_rcg2_is_enabled(struct clk_hw *hw)
|
|
{
|
|
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
|
u32 cmd;
|
|
int ret;
|
|
|
|
ret = regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, &cmd);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return (cmd & CMD_ROOT_OFF) == 0;
|
|
}
|
|
|
|
static u8 clk_rcg2_get_parent(struct clk_hw *hw)
|
|
{
|
|
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
|
int num_parents = clk_hw_get_num_parents(hw);
|
|
u32 cfg;
|
|
int i, ret;
|
|
|
|
ret = regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg);
|
|
if (ret)
|
|
goto err;
|
|
|
|
cfg &= CFG_SRC_SEL_MASK;
|
|
cfg >>= CFG_SRC_SEL_SHIFT;
|
|
|
|
for (i = 0; i < num_parents; i++)
|
|
if (cfg == rcg->parent_map[i].cfg)
|
|
return i;
|
|
|
|
err:
|
|
pr_debug("%s: Clock %s has invalid parent, using default.\n",
|
|
__func__, clk_hw_get_name(hw));
|
|
return 0;
|
|
}
|
|
|
|
static int update_config(struct clk_rcg2 *rcg)
|
|
{
|
|
int count, ret;
|
|
u32 cmd;
|
|
struct clk_hw *hw = &rcg->clkr.hw;
|
|
const char *name = clk_hw_get_name(hw);
|
|
|
|
ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG,
|
|
CMD_UPDATE, CMD_UPDATE);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Wait for update to take effect */
|
|
for (count = 500; count > 0; count--) {
|
|
ret = regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, &cmd);
|
|
if (ret)
|
|
return ret;
|
|
if (!(cmd & CMD_UPDATE))
|
|
return 0;
|
|
udelay(1);
|
|
}
|
|
|
|
WARN(1, "%s: rcg didn't update its configuration.", name);
|
|
return -EBUSY;
|
|
}
|
|
|
|
static int clk_rcg2_set_parent(struct clk_hw *hw, u8 index)
|
|
{
|
|
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
|
int ret;
|
|
u32 cfg = rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT;
|
|
|
|
ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG,
|
|
CFG_SRC_SEL_MASK, cfg);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return update_config(rcg);
|
|
}
|
|
|
|
/*
|
|
* Calculate m/n:d rate
|
|
*
|
|
* parent_rate m
|
|
* rate = ----------- x ---
|
|
* hid_div n
|
|
*/
|
|
static unsigned long
|
|
calc_rate(unsigned long rate, u32 m, u32 n, u32 mode, u32 hid_div)
|
|
{
|
|
if (hid_div) {
|
|
rate *= 2;
|
|
rate /= hid_div + 1;
|
|
}
|
|
|
|
if (mode) {
|
|
u64 tmp = rate;
|
|
tmp *= m;
|
|
do_div(tmp, n);
|
|
rate = tmp;
|
|
}
|
|
|
|
return rate;
|
|
}
|
|
|
|
static unsigned long
|
|
clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
|
|
{
|
|
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
|
u32 cfg, hid_div, m = 0, n = 0, mode = 0, mask;
|
|
|
|
regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg);
|
|
|
|
if (rcg->mnd_width) {
|
|
mask = BIT(rcg->mnd_width) - 1;
|
|
regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + M_REG, &m);
|
|
m &= mask;
|
|
regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + N_REG, &n);
|
|
n = ~n;
|
|
n &= mask;
|
|
n += m;
|
|
mode = cfg & CFG_MODE_MASK;
|
|
mode >>= CFG_MODE_SHIFT;
|
|
}
|
|
|
|
mask = BIT(rcg->hid_width) - 1;
|
|
hid_div = cfg >> CFG_SRC_DIV_SHIFT;
|
|
hid_div &= mask;
|
|
|
|
return calc_rate(parent_rate, m, n, mode, hid_div);
|
|
}
|
|
|
|
static int _freq_tbl_determine_rate(struct clk_hw *hw,
|
|
const struct freq_tbl *f, struct clk_rate_request *req)
|
|
{
|
|
unsigned long clk_flags, rate = req->rate;
|
|
struct clk_hw *p;
|
|
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
|
int index;
|
|
|
|
f = qcom_find_freq(f, rate);
|
|
if (!f)
|
|
return -EINVAL;
|
|
|
|
index = qcom_find_src_index(hw, rcg->parent_map, f->src);
|
|
if (index < 0)
|
|
return index;
|
|
|
|
clk_flags = clk_hw_get_flags(hw);
|
|
p = clk_hw_get_parent_by_index(hw, index);
|
|
if (!p)
|
|
return -EINVAL;
|
|
|
|
if (clk_flags & CLK_SET_RATE_PARENT) {
|
|
if (f->pre_div) {
|
|
if (!rate)
|
|
rate = req->rate;
|
|
rate /= 2;
|
|
rate *= f->pre_div + 1;
|
|
}
|
|
|
|
if (f->n) {
|
|
u64 tmp = rate;
|
|
tmp = tmp * f->n;
|
|
do_div(tmp, f->m);
|
|
rate = tmp;
|
|
}
|
|
} else {
|
|
rate = clk_hw_get_rate(p);
|
|
}
|
|
req->best_parent_hw = p;
|
|
req->best_parent_rate = rate;
|
|
req->rate = f->freq;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int clk_rcg2_determine_rate(struct clk_hw *hw,
|
|
struct clk_rate_request *req)
|
|
{
|
|
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
|
|
|
return _freq_tbl_determine_rate(hw, rcg->freq_tbl, req);
|
|
}
|
|
|
|
static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f)
|
|
{
|
|
u32 cfg, mask;
|
|
struct clk_hw *hw = &rcg->clkr.hw;
|
|
int ret, index = qcom_find_src_index(hw, rcg->parent_map, f->src);
|
|
|
|
if (index < 0)
|
|
return index;
|
|
|
|
if (rcg->mnd_width && f->n) {
|
|
mask = BIT(rcg->mnd_width) - 1;
|
|
ret = regmap_update_bits(rcg->clkr.regmap,
|
|
rcg->cmd_rcgr + M_REG, mask, f->m);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = regmap_update_bits(rcg->clkr.regmap,
|
|
rcg->cmd_rcgr + N_REG, mask, ~(f->n - f->m));
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = regmap_update_bits(rcg->clkr.regmap,
|
|
rcg->cmd_rcgr + D_REG, mask, ~f->n);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
mask = BIT(rcg->hid_width) - 1;
|
|
mask |= CFG_SRC_SEL_MASK | CFG_MODE_MASK;
|
|
cfg = f->pre_div << CFG_SRC_DIV_SHIFT;
|
|
cfg |= rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT;
|
|
if (rcg->mnd_width && f->n && (f->m != f->n))
|
|
cfg |= CFG_MODE_DUAL_EDGE;
|
|
ret = regmap_update_bits(rcg->clkr.regmap,
|
|
rcg->cmd_rcgr + CFG_REG, mask, cfg);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return update_config(rcg);
|
|
}
|
|
|
|
static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate)
|
|
{
|
|
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
|
const struct freq_tbl *f;
|
|
|
|
f = qcom_find_freq(rcg->freq_tbl, rate);
|
|
if (!f)
|
|
return -EINVAL;
|
|
|
|
return clk_rcg2_configure(rcg, f);
|
|
}
|
|
|
|
static int clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
|
|
unsigned long parent_rate)
|
|
{
|
|
return __clk_rcg2_set_rate(hw, rate);
|
|
}
|
|
|
|
static int clk_rcg2_set_rate_and_parent(struct clk_hw *hw,
|
|
unsigned long rate, unsigned long parent_rate, u8 index)
|
|
{
|
|
return __clk_rcg2_set_rate(hw, rate);
|
|
}
|
|
|
|
const struct clk_ops clk_rcg2_ops = {
|
|
.is_enabled = clk_rcg2_is_enabled,
|
|
.get_parent = clk_rcg2_get_parent,
|
|
.set_parent = clk_rcg2_set_parent,
|
|
.recalc_rate = clk_rcg2_recalc_rate,
|
|
.determine_rate = clk_rcg2_determine_rate,
|
|
.set_rate = clk_rcg2_set_rate,
|
|
.set_rate_and_parent = clk_rcg2_set_rate_and_parent,
|
|
};
|
|
EXPORT_SYMBOL_GPL(clk_rcg2_ops);
|
|
|
|
static int clk_rcg2_shared_force_enable(struct clk_hw *hw, unsigned long rate)
|
|
{
|
|
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
|
const char *name = clk_hw_get_name(hw);
|
|
int ret, count;
|
|
|
|
/* force enable RCG */
|
|
ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG,
|
|
CMD_ROOT_EN, CMD_ROOT_EN);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* wait for RCG to turn ON */
|
|
for (count = 500; count > 0; count--) {
|
|
ret = clk_rcg2_is_enabled(hw);
|
|
if (ret)
|
|
break;
|
|
udelay(1);
|
|
}
|
|
if (!count)
|
|
pr_err("%s: RCG did not turn on\n", name);
|
|
|
|
/* set clock rate */
|
|
ret = __clk_rcg2_set_rate(hw, rate);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* clear force enable RCG */
|
|
return regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG,
|
|
CMD_ROOT_EN, 0);
|
|
}
|
|
|
|
static int clk_rcg2_shared_set_rate(struct clk_hw *hw, unsigned long rate,
|
|
unsigned long parent_rate)
|
|
{
|
|
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
|
|
|
/* cache the rate */
|
|
rcg->current_freq = rate;
|
|
|
|
if (!__clk_is_enabled(hw->clk))
|
|
return 0;
|
|
|
|
return clk_rcg2_shared_force_enable(hw, rcg->current_freq);
|
|
}
|
|
|
|
static unsigned long
|
|
clk_rcg2_shared_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
|
|
{
|
|
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
|
|
|
return rcg->current_freq = clk_rcg2_recalc_rate(hw, parent_rate);
|
|
}
|
|
|
|
static int clk_rcg2_shared_enable(struct clk_hw *hw)
|
|
{
|
|
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
|
|
|
return clk_rcg2_shared_force_enable(hw, rcg->current_freq);
|
|
}
|
|
|
|
static void clk_rcg2_shared_disable(struct clk_hw *hw)
|
|
{
|
|
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
|
|
|
/* switch to XO, which is the lowest entry in the freq table */
|
|
clk_rcg2_shared_set_rate(hw, rcg->freq_tbl[0].freq, 0);
|
|
}
|
|
|
|
const struct clk_ops clk_rcg2_shared_ops = {
|
|
.enable = clk_rcg2_shared_enable,
|
|
.disable = clk_rcg2_shared_disable,
|
|
.get_parent = clk_rcg2_get_parent,
|
|
.recalc_rate = clk_rcg2_shared_recalc_rate,
|
|
.determine_rate = clk_rcg2_determine_rate,
|
|
.set_rate = clk_rcg2_shared_set_rate,
|
|
};
|
|
EXPORT_SYMBOL_GPL(clk_rcg2_shared_ops);
|
|
|
|
struct frac_entry {
|
|
int num;
|
|
int den;
|
|
};
|
|
|
|
static const struct frac_entry frac_table_675m[] = { /* link rate of 270M */
|
|
{ 52, 295 }, /* 119 M */
|
|
{ 11, 57 }, /* 130.25 M */
|
|
{ 63, 307 }, /* 138.50 M */
|
|
{ 11, 50 }, /* 148.50 M */
|
|
{ 47, 206 }, /* 154 M */
|
|
{ 31, 100 }, /* 205.25 M */
|
|
{ 107, 269 }, /* 268.50 M */
|
|
{ },
|
|
};
|
|
|
|
static struct frac_entry frac_table_810m[] = { /* Link rate of 162M */
|
|
{ 31, 211 }, /* 119 M */
|
|
{ 32, 199 }, /* 130.25 M */
|
|
{ 63, 307 }, /* 138.50 M */
|
|
{ 11, 60 }, /* 148.50 M */
|
|
{ 50, 263 }, /* 154 M */
|
|
{ 31, 120 }, /* 205.25 M */
|
|
{ 119, 359 }, /* 268.50 M */
|
|
{ },
|
|
};
|
|
|
|
static int clk_edp_pixel_set_rate(struct clk_hw *hw, unsigned long rate,
|
|
unsigned long parent_rate)
|
|
{
|
|
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
|
struct freq_tbl f = *rcg->freq_tbl;
|
|
const struct frac_entry *frac;
|
|
int delta = 100000;
|
|
s64 src_rate = parent_rate;
|
|
s64 request;
|
|
u32 mask = BIT(rcg->hid_width) - 1;
|
|
u32 hid_div;
|
|
|
|
if (src_rate == 810000000)
|
|
frac = frac_table_810m;
|
|
else
|
|
frac = frac_table_675m;
|
|
|
|
for (; frac->num; frac++) {
|
|
request = rate;
|
|
request *= frac->den;
|
|
request = div_s64(request, frac->num);
|
|
if ((src_rate < (request - delta)) ||
|
|
(src_rate > (request + delta)))
|
|
continue;
|
|
|
|
regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG,
|
|
&hid_div);
|
|
f.pre_div = hid_div;
|
|
f.pre_div >>= CFG_SRC_DIV_SHIFT;
|
|
f.pre_div &= mask;
|
|
f.m = frac->num;
|
|
f.n = frac->den;
|
|
|
|
return clk_rcg2_configure(rcg, &f);
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int clk_edp_pixel_set_rate_and_parent(struct clk_hw *hw,
|
|
unsigned long rate, unsigned long parent_rate, u8 index)
|
|
{
|
|
/* Parent index is set statically in frequency table */
|
|
return clk_edp_pixel_set_rate(hw, rate, parent_rate);
|
|
}
|
|
|
|
static int clk_edp_pixel_determine_rate(struct clk_hw *hw,
|
|
struct clk_rate_request *req)
|
|
{
|
|
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
|
const struct freq_tbl *f = rcg->freq_tbl;
|
|
const struct frac_entry *frac;
|
|
int delta = 100000;
|
|
s64 request;
|
|
u32 mask = BIT(rcg->hid_width) - 1;
|
|
u32 hid_div;
|
|
int index = qcom_find_src_index(hw, rcg->parent_map, f->src);
|
|
|
|
/* Force the correct parent */
|
|
req->best_parent_hw = clk_hw_get_parent_by_index(hw, index);
|
|
req->best_parent_rate = clk_hw_get_rate(req->best_parent_hw);
|
|
|
|
if (req->best_parent_rate == 810000000)
|
|
frac = frac_table_810m;
|
|
else
|
|
frac = frac_table_675m;
|
|
|
|
for (; frac->num; frac++) {
|
|
request = req->rate;
|
|
request *= frac->den;
|
|
request = div_s64(request, frac->num);
|
|
if ((req->best_parent_rate < (request - delta)) ||
|
|
(req->best_parent_rate > (request + delta)))
|
|
continue;
|
|
|
|
regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG,
|
|
&hid_div);
|
|
hid_div >>= CFG_SRC_DIV_SHIFT;
|
|
hid_div &= mask;
|
|
|
|
req->rate = calc_rate(req->best_parent_rate,
|
|
frac->num, frac->den,
|
|
!!frac->den, hid_div);
|
|
return 0;
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
const struct clk_ops clk_edp_pixel_ops = {
|
|
.is_enabled = clk_rcg2_is_enabled,
|
|
.get_parent = clk_rcg2_get_parent,
|
|
.set_parent = clk_rcg2_set_parent,
|
|
.recalc_rate = clk_rcg2_recalc_rate,
|
|
.set_rate = clk_edp_pixel_set_rate,
|
|
.set_rate_and_parent = clk_edp_pixel_set_rate_and_parent,
|
|
.determine_rate = clk_edp_pixel_determine_rate,
|
|
};
|
|
EXPORT_SYMBOL_GPL(clk_edp_pixel_ops);
|
|
|
|
static int clk_byte_determine_rate(struct clk_hw *hw,
|
|
struct clk_rate_request *req)
|
|
{
|
|
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
|
const struct freq_tbl *f = rcg->freq_tbl;
|
|
int index = qcom_find_src_index(hw, rcg->parent_map, f->src);
|
|
unsigned long parent_rate, div;
|
|
u32 mask = BIT(rcg->hid_width) - 1;
|
|
struct clk_hw *p;
|
|
|
|
if (req->rate == 0)
|
|
return -EINVAL;
|
|
|
|
req->best_parent_hw = p = clk_hw_get_parent_by_index(hw, index);
|
|
req->best_parent_rate = parent_rate = clk_hw_round_rate(p, req->rate);
|
|
|
|
div = DIV_ROUND_UP((2 * parent_rate), req->rate) - 1;
|
|
div = min_t(u32, div, mask);
|
|
|
|
req->rate = calc_rate(parent_rate, 0, 0, 0, div);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int clk_byte_set_rate(struct clk_hw *hw, unsigned long rate,
|
|
unsigned long parent_rate)
|
|
{
|
|
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
|
struct freq_tbl f = *rcg->freq_tbl;
|
|
unsigned long div;
|
|
u32 mask = BIT(rcg->hid_width) - 1;
|
|
|
|
div = DIV_ROUND_UP((2 * parent_rate), rate) - 1;
|
|
div = min_t(u32, div, mask);
|
|
|
|
f.pre_div = div;
|
|
|
|
return clk_rcg2_configure(rcg, &f);
|
|
}
|
|
|
|
static int clk_byte_set_rate_and_parent(struct clk_hw *hw,
|
|
unsigned long rate, unsigned long parent_rate, u8 index)
|
|
{
|
|
/* Parent index is set statically in frequency table */
|
|
return clk_byte_set_rate(hw, rate, parent_rate);
|
|
}
|
|
|
|
const struct clk_ops clk_byte_ops = {
|
|
.is_enabled = clk_rcg2_is_enabled,
|
|
.get_parent = clk_rcg2_get_parent,
|
|
.set_parent = clk_rcg2_set_parent,
|
|
.recalc_rate = clk_rcg2_recalc_rate,
|
|
.set_rate = clk_byte_set_rate,
|
|
.set_rate_and_parent = clk_byte_set_rate_and_parent,
|
|
.determine_rate = clk_byte_determine_rate,
|
|
};
|
|
EXPORT_SYMBOL_GPL(clk_byte_ops);
|
|
|
|
static int clk_byte2_determine_rate(struct clk_hw *hw,
|
|
struct clk_rate_request *req)
|
|
{
|
|
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
|
unsigned long parent_rate, div;
|
|
u32 mask = BIT(rcg->hid_width) - 1;
|
|
struct clk_hw *p;
|
|
unsigned long rate = req->rate;
|
|
|
|
if (rate == 0)
|
|
return -EINVAL;
|
|
|
|
p = req->best_parent_hw;
|
|
req->best_parent_rate = parent_rate = clk_hw_round_rate(p, rate);
|
|
|
|
div = DIV_ROUND_UP((2 * parent_rate), rate) - 1;
|
|
div = min_t(u32, div, mask);
|
|
|
|
req->rate = calc_rate(parent_rate, 0, 0, 0, div);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int clk_byte2_set_rate(struct clk_hw *hw, unsigned long rate,
|
|
unsigned long parent_rate)
|
|
{
|
|
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
|
struct freq_tbl f = { 0 };
|
|
unsigned long div;
|
|
int i, num_parents = clk_hw_get_num_parents(hw);
|
|
u32 mask = BIT(rcg->hid_width) - 1;
|
|
u32 cfg;
|
|
|
|
div = DIV_ROUND_UP((2 * parent_rate), rate) - 1;
|
|
div = min_t(u32, div, mask);
|
|
|
|
f.pre_div = div;
|
|
|
|
regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg);
|
|
cfg &= CFG_SRC_SEL_MASK;
|
|
cfg >>= CFG_SRC_SEL_SHIFT;
|
|
|
|
for (i = 0; i < num_parents; i++) {
|
|
if (cfg == rcg->parent_map[i].cfg) {
|
|
f.src = rcg->parent_map[i].src;
|
|
return clk_rcg2_configure(rcg, &f);
|
|
}
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int clk_byte2_set_rate_and_parent(struct clk_hw *hw,
|
|
unsigned long rate, unsigned long parent_rate, u8 index)
|
|
{
|
|
/* Read the hardware to determine parent during set_rate */
|
|
return clk_byte2_set_rate(hw, rate, parent_rate);
|
|
}
|
|
|
|
const struct clk_ops clk_byte2_ops = {
|
|
.is_enabled = clk_rcg2_is_enabled,
|
|
.get_parent = clk_rcg2_get_parent,
|
|
.set_parent = clk_rcg2_set_parent,
|
|
.recalc_rate = clk_rcg2_recalc_rate,
|
|
.set_rate = clk_byte2_set_rate,
|
|
.set_rate_and_parent = clk_byte2_set_rate_and_parent,
|
|
.determine_rate = clk_byte2_determine_rate,
|
|
};
|
|
EXPORT_SYMBOL_GPL(clk_byte2_ops);
|
|
|
|
static const struct frac_entry frac_table_pixel[] = {
|
|
{ 3, 8 },
|
|
{ 2, 9 },
|
|
{ 4, 9 },
|
|
{ 1, 1 },
|
|
{ 2, 3 },
|
|
{ }
|
|
};
|
|
|
|
static int clk_pixel_determine_rate(struct clk_hw *hw,
|
|
struct clk_rate_request *req)
|
|
{
|
|
unsigned long request, src_rate;
|
|
int delta = 100000;
|
|
const struct frac_entry *frac = frac_table_pixel;
|
|
|
|
for (; frac->num; frac++) {
|
|
request = (req->rate * frac->den) / frac->num;
|
|
|
|
src_rate = clk_hw_round_rate(req->best_parent_hw, request);
|
|
if ((src_rate < (request - delta)) ||
|
|
(src_rate > (request + delta)))
|
|
continue;
|
|
|
|
req->best_parent_rate = src_rate;
|
|
req->rate = (src_rate * frac->num) / frac->den;
|
|
return 0;
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int clk_pixel_set_rate(struct clk_hw *hw, unsigned long rate,
|
|
unsigned long parent_rate)
|
|
{
|
|
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
|
struct freq_tbl f = { 0 };
|
|
const struct frac_entry *frac = frac_table_pixel;
|
|
unsigned long request;
|
|
int delta = 100000;
|
|
u32 mask = BIT(rcg->hid_width) - 1;
|
|
u32 hid_div, cfg;
|
|
int i, num_parents = clk_hw_get_num_parents(hw);
|
|
|
|
regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg);
|
|
cfg &= CFG_SRC_SEL_MASK;
|
|
cfg >>= CFG_SRC_SEL_SHIFT;
|
|
|
|
for (i = 0; i < num_parents; i++)
|
|
if (cfg == rcg->parent_map[i].cfg) {
|
|
f.src = rcg->parent_map[i].src;
|
|
break;
|
|
}
|
|
|
|
for (; frac->num; frac++) {
|
|
request = (rate * frac->den) / frac->num;
|
|
|
|
if ((parent_rate < (request - delta)) ||
|
|
(parent_rate > (request + delta)))
|
|
continue;
|
|
|
|
regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG,
|
|
&hid_div);
|
|
f.pre_div = hid_div;
|
|
f.pre_div >>= CFG_SRC_DIV_SHIFT;
|
|
f.pre_div &= mask;
|
|
f.m = frac->num;
|
|
f.n = frac->den;
|
|
|
|
return clk_rcg2_configure(rcg, &f);
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int clk_pixel_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
|
|
unsigned long parent_rate, u8 index)
|
|
{
|
|
return clk_pixel_set_rate(hw, rate, parent_rate);
|
|
}
|
|
|
|
const struct clk_ops clk_pixel_ops = {
|
|
.is_enabled = clk_rcg2_is_enabled,
|
|
.get_parent = clk_rcg2_get_parent,
|
|
.set_parent = clk_rcg2_set_parent,
|
|
.recalc_rate = clk_rcg2_recalc_rate,
|
|
.set_rate = clk_pixel_set_rate,
|
|
.set_rate_and_parent = clk_pixel_set_rate_and_parent,
|
|
.determine_rate = clk_pixel_determine_rate,
|
|
};
|
|
EXPORT_SYMBOL_GPL(clk_pixel_ops);
|
|
|
|
static int clk_gfx3d_determine_rate(struct clk_hw *hw,
|
|
struct clk_rate_request *req)
|
|
{
|
|
struct clk_rate_request parent_req = { };
|
|
struct clk_hw *p2, *p8, *p9, *xo;
|
|
unsigned long p9_rate;
|
|
int ret;
|
|
|
|
xo = clk_hw_get_parent_by_index(hw, 0);
|
|
if (req->rate == clk_hw_get_rate(xo)) {
|
|
req->best_parent_hw = xo;
|
|
return 0;
|
|
}
|
|
|
|
p9 = clk_hw_get_parent_by_index(hw, 2);
|
|
p2 = clk_hw_get_parent_by_index(hw, 3);
|
|
p8 = clk_hw_get_parent_by_index(hw, 4);
|
|
|
|
/* PLL9 is a fixed rate PLL */
|
|
p9_rate = clk_hw_get_rate(p9);
|
|
|
|
parent_req.rate = req->rate = min(req->rate, p9_rate);
|
|
if (req->rate == p9_rate) {
|
|
req->rate = req->best_parent_rate = p9_rate;
|
|
req->best_parent_hw = p9;
|
|
return 0;
|
|
}
|
|
|
|
if (req->best_parent_hw == p9) {
|
|
/* Are we going back to a previously used rate? */
|
|
if (clk_hw_get_rate(p8) == req->rate)
|
|
req->best_parent_hw = p8;
|
|
else
|
|
req->best_parent_hw = p2;
|
|
} else if (req->best_parent_hw == p8) {
|
|
req->best_parent_hw = p2;
|
|
} else {
|
|
req->best_parent_hw = p8;
|
|
}
|
|
|
|
ret = __clk_determine_rate(req->best_parent_hw, &parent_req);
|
|
if (ret)
|
|
return ret;
|
|
|
|
req->rate = req->best_parent_rate = parent_req.rate;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int clk_gfx3d_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
|
|
unsigned long parent_rate, u8 index)
|
|
{
|
|
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
|
|
u32 cfg;
|
|
int ret;
|
|
|
|
/* Just mux it, we don't use the division or m/n hardware */
|
|
cfg = rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT;
|
|
ret = regmap_write(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, cfg);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return update_config(rcg);
|
|
}
|
|
|
|
static int clk_gfx3d_set_rate(struct clk_hw *hw, unsigned long rate,
|
|
unsigned long parent_rate)
|
|
{
|
|
/*
|
|
* We should never get here; clk_gfx3d_determine_rate() should always
|
|
* make us use a different parent than what we're currently using, so
|
|
* clk_gfx3d_set_rate_and_parent() should always be called.
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
const struct clk_ops clk_gfx3d_ops = {
|
|
.is_enabled = clk_rcg2_is_enabled,
|
|
.get_parent = clk_rcg2_get_parent,
|
|
.set_parent = clk_rcg2_set_parent,
|
|
.recalc_rate = clk_rcg2_recalc_rate,
|
|
.set_rate = clk_gfx3d_set_rate,
|
|
.set_rate_and_parent = clk_gfx3d_set_rate_and_parent,
|
|
.determine_rate = clk_gfx3d_determine_rate,
|
|
};
|
|
EXPORT_SYMBOL_GPL(clk_gfx3d_ops);
|