1
0
Files
Greg Kroah-Hartman f95d85c34b Merge 4.9.311 into android-4.9-q
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
2022-04-25 16:25:23 +03:00

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);