Commit 12ef3933 authored by Stephen Boyd's avatar Stephen Boyd

Merge branch 'clk-bcm' into clk-next

 - Make defines for bcm63xx-gate clks to use in DT
 - Support gate clks on BCM6318 SoCs
 - Add HDMI clks for BCM2711 SoCs
 - Support BCM2711 SoC firmware clks

* clk-bcm: (42 commits)
  clk: bcm: dvp: Add missing module informations
  clk: bcm: rpi: Remove the quirks for the CPU clock
  clk: bcm2835: Don't cache the PLLB rate
  clk: bcm2835: Allow custom CCF flags for the PLLs
  Revert "clk: bcm2835: remove pllb"
  clk: bcm: rpi: Give firmware clocks a name
  clk: bcm: rpi: Discover the firmware clocks
  clk: bcm: rpi: Add an enum for the firmware clocks
  clk: bcm: rpi: Add DT provider for the clocks
  clk: bcm: rpi: Make the PLLB registration function return a clk_hw
  clk: bcm: rpi: Split pllb clock hooks
  clk: bcm: rpi: Rename is_prepared function
  clk: bcm: rpi: Pass the clocks data to the firmware function
  clk: bcm: rpi: Add clock id to data
  clk: bcm: rpi: Create a data structure for the clocks
  clk: bcm: rpi: Use CCF boundaries instead of rolling our own
  clk: bcm: rpi: Make sure the clkdev lookup is removed
  clk: bcm: rpi: Switch to clk_hw_register_clkdev
  clk: bcm: rpi: Remove pllb_arm_lookup global pointer
  clk: bcm: rpi: Make sure pllb_arm is removed
  ...
parents 7aae3c16 eb46f547
Raspberry Pi VideoCore firmware driver
Required properties:
- compatible: Should be "raspberrypi,bcm2835-firmware"
- mboxes: Phandle to the firmware device's Mailbox.
(See: ../mailbox/mailbox.txt for more information)
Example:
firmware {
compatible = "raspberrypi,bcm2835-firmware";
mboxes = <&mailbox>;
};
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/arm/bcm/raspberrypi,bcm2835-firmware.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Raspberry Pi VideoCore firmware driver
maintainers:
- Eric Anholt <eric@anholt.net>
- Stefan Wahren <wahrenst@gmx.net>
properties:
compatible:
items:
- const: raspberrypi,bcm2835-firmware
- const: simple-bus
mboxes:
$ref: '/schemas/types.yaml#/definitions/phandle'
description: |
Phandle to the firmware device's Mailbox.
(See: ../mailbox/mailbox.txt for more information)
clocks:
type: object
properties:
compatible:
const: raspberrypi,firmware-clocks
"#clock-cells":
const: 1
description: >
The argument is the ID of the clocks contained by the
firmware messages.
required:
- compatible
- "#clock-cells"
additionalProperties: false
required:
- compatible
- mboxes
examples:
- |
firmware {
compatible = "raspberrypi,bcm2835-firmware", "simple-bus";
mboxes = <&mailbox>;
firmware_clocks: clocks {
compatible = "raspberrypi,firmware-clocks";
#clock-cells = <1>;
};
};
...
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/clock/brcm,bcm2711-dvp.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Broadcom BCM2711 HDMI DVP Device Tree Bindings
maintainers:
- Maxime Ripard <mripard@kernel.org>
properties:
"#clock-cells":
const: 1
"#reset-cells":
const: 1
compatible:
const: brcm,brcm2711-dvp
reg:
maxItems: 1
clocks:
maxItems: 1
required:
- "#clock-cells"
- "#reset-cells"
- compatible
- reg
- clocks
additionalProperties: false
examples:
- |
dvp: clock@7ef00000 {
compatible = "brcm,brcm2711-dvp";
reg = <0x7ef00000 0x10>;
clocks = <&clk_108MHz>;
#clock-cells = <1>;
#reset-cells = <1>;
};
...
......@@ -3,6 +3,8 @@ Gated Clock Controller Bindings for MIPS based BCM63XX SoCs
Required properties:
- compatible: must be one of:
"brcm,bcm3368-clocks"
"brcm,bcm6318-clocks"
"brcm,bcm6318-ubus-clocks"
"brcm,bcm6328-clocks"
"brcm,bcm6358-clocks"
"brcm,bcm6362-clocks"
......
# SPDX-License-Identifier: GPL-2.0-only
config CLK_BCM2711_DVP
tristate "Broadcom BCM2711 DVP support"
depends on ARCH_BCM2835 ||COMPILE_TEST
depends on COMMON_CLK
default ARCH_BCM2835
select RESET_SIMPLE
help
Enable common clock framework support for the Broadcom BCM2711
DVP Controller.
config CLK_BCM2835
bool "Broadcom BCM2835 clock support"
depends on ARCH_BCM2835 || ARCH_BRCMSTB || COMPILE_TEST
......
......@@ -6,6 +6,7 @@ obj-$(CONFIG_CLK_BCM_KONA) += clk-kona-setup.o
obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm281xx.o
obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm21664.o
obj-$(CONFIG_COMMON_CLK_IPROC) += clk-iproc-armpll.o clk-iproc-pll.o clk-iproc-asiu.o
obj-$(CONFIG_CLK_BCM2711_DVP) += clk-bcm2711-dvp.o
obj-$(CONFIG_CLK_BCM2835) += clk-bcm2835.o
obj-$(CONFIG_CLK_BCM2835) += clk-bcm2835-aux.o
obj-$(CONFIG_CLK_RASPBERRYPI) += clk-raspberrypi.o
......
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright 2020 Cerno
#include <linux/clk-provider.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/reset-controller.h>
#include <linux/reset/reset-simple.h>
#define DVP_HT_RPI_SW_INIT 0x04
#define DVP_HT_RPI_MISC_CONFIG 0x08
#define NR_CLOCKS 2
#define NR_RESETS 6
struct clk_dvp {
struct clk_hw_onecell_data *data;
struct reset_simple_data reset;
};
static const struct clk_parent_data clk_dvp_parent = {
.index = 0,
};
static int clk_dvp_probe(struct platform_device *pdev)
{
struct clk_hw_onecell_data *data;
struct resource *res;
struct clk_dvp *dvp;
void __iomem *base;
int ret;
dvp = devm_kzalloc(&pdev->dev, sizeof(*dvp), GFP_KERNEL);
if (!dvp)
return -ENOMEM;
platform_set_drvdata(pdev, dvp);
dvp->data = devm_kzalloc(&pdev->dev,
struct_size(dvp->data, hws, NR_CLOCKS),
GFP_KERNEL);
if (!dvp->data)
return -ENOMEM;
data = dvp->data;
base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(base))
return PTR_ERR(base);
dvp->reset.rcdev.owner = THIS_MODULE;
dvp->reset.rcdev.nr_resets = NR_RESETS;
dvp->reset.rcdev.ops = &reset_simple_ops;
dvp->reset.rcdev.of_node = pdev->dev.of_node;
dvp->reset.membase = base + DVP_HT_RPI_SW_INIT;
spin_lock_init(&dvp->reset.lock);
ret = devm_reset_controller_register(&pdev->dev, &dvp->reset.rcdev);
if (ret)
return ret;
data->hws[0] = clk_hw_register_gate_parent_data(&pdev->dev,
"hdmi0-108MHz",
&clk_dvp_parent, 0,
base + DVP_HT_RPI_MISC_CONFIG, 3,
CLK_GATE_SET_TO_DISABLE,
&dvp->reset.lock);
if (IS_ERR(data->hws[0]))
return PTR_ERR(data->hws[0]);
data->hws[1] = clk_hw_register_gate_parent_data(&pdev->dev,
"hdmi1-108MHz",
&clk_dvp_parent, 0,
base + DVP_HT_RPI_MISC_CONFIG, 4,
CLK_GATE_SET_TO_DISABLE,
&dvp->reset.lock);
if (IS_ERR(data->hws[1])) {
ret = PTR_ERR(data->hws[1]);
goto unregister_clk0;
}
data->num = NR_CLOCKS;
ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get,
data);
if (ret)
goto unregister_clk1;
return 0;
unregister_clk1:
clk_hw_unregister_gate(data->hws[1]);
unregister_clk0:
clk_hw_unregister_gate(data->hws[0]);
return ret;
};
static int clk_dvp_remove(struct platform_device *pdev)
{
struct clk_dvp *dvp = platform_get_drvdata(pdev);
struct clk_hw_onecell_data *data = dvp->data;
clk_hw_unregister_gate(data->hws[1]);
clk_hw_unregister_gate(data->hws[0]);
return 0;
}
static const struct of_device_id clk_dvp_dt_ids[] = {
{ .compatible = "brcm,brcm2711-dvp", },
{ /* sentinel */ }
};
static struct platform_driver clk_dvp_driver = {
.probe = clk_dvp_probe,
.remove = clk_dvp_remove,
.driver = {
.name = "brcm2711-dvp",
.of_match_table = clk_dvp_dt_ids,
},
};
module_platform_driver(clk_dvp_driver);
MODULE_AUTHOR("Maxime Ripard <maxime@cerno.tech>");
MODULE_DESCRIPTION("BCM2711 DVP clock driver");
MODULE_LICENSE("GPL");
......@@ -421,6 +421,7 @@ struct bcm2835_pll_data {
u32 reference_enable_mask;
/* Bit in CM_LOCK to indicate when the PLL has locked. */
u32 lock_mask;
u32 flags;
const struct bcm2835_pll_ana_bits *ana;
......@@ -1310,7 +1311,7 @@ static struct clk_hw *bcm2835_register_pll(struct bcm2835_cprman *cprman,
init.num_parents = 1;
init.name = pll_data->name;
init.ops = &bcm2835_pll_clk_ops;
init.flags = CLK_IGNORE_UNUSED;
init.flags = pll_data->flags | CLK_IGNORE_UNUSED;
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
if (!pll)
......@@ -1684,10 +1685,33 @@ static const struct bcm2835_clk_desc clk_desc_array[] = {
.fixed_divider = 1,
.flags = CLK_SET_RATE_PARENT),
/*
* PLLB is used for the ARM's clock. Controlled by firmware, see
* clk-raspberrypi.c.
*/
/* PLLB is used for the ARM's clock. */
[BCM2835_PLLB] = REGISTER_PLL(
SOC_ALL,
.name = "pllb",
.cm_ctrl_reg = CM_PLLB,
.a2w_ctrl_reg = A2W_PLLB_CTRL,
.frac_reg = A2W_PLLB_FRAC,
.ana_reg_base = A2W_PLLB_ANA0,
.reference_enable_mask = A2W_XOSC_CTRL_PLLB_ENABLE,
.lock_mask = CM_LOCK_FLOCKB,
.ana = &bcm2835_ana_default,
.min_rate = 600000000u,
.max_rate = 3000000000u,
.max_fb_rate = BCM2835_MAX_FB_RATE,
.flags = CLK_GET_RATE_NOCACHE),
[BCM2835_PLLB_ARM] = REGISTER_PLL_DIV(
SOC_ALL,
.name = "pllb_arm",
.source_pll = "pllb",
.cm_reg = CM_PLLB,
.a2w_reg = A2W_PLLB_ARM,
.load_mask = CM_PLLB_LOADARM,
.hold_mask = CM_PLLB_HOLDARM,
.fixed_divider = 1,
.flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE),
/*
* PLLC is the core PLL, used to drive the core VPU clock.
......
This diff is collapsed.
This diff is collapsed.
......@@ -208,6 +208,20 @@ rpi_register_hwmon_driver(struct device *dev, struct rpi_firmware *fw)
static void rpi_register_clk_driver(struct device *dev)
{
struct device_node *firmware;
/*
* Earlier DTs don't have a node for the firmware clocks but
* rely on us creating a platform device by hand. If we do
* have a node for the firmware clocks, just bail out here.
*/
firmware = of_get_compatible_child(dev->of_node,
"raspberrypi,firmware-clocks");
if (firmware) {
of_node_put(firmware);
return;
}
rpi_clk = platform_device_register_data(dev, "raspberrypi-clk",
-1, NULL, 0);
}
......
......@@ -11,6 +11,7 @@
* Maxime Ripard <maxime.ripard@free-electrons.com>
*/
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/io.h>
......@@ -18,10 +19,9 @@
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/reset-controller.h>
#include <linux/reset/reset-simple.h>
#include <linux/spinlock.h>
#include "reset-simple.h"
static inline struct reset_simple_data *
to_reset_simple_data(struct reset_controller_dev *rcdev)
{
......@@ -64,6 +64,24 @@ static int reset_simple_deassert(struct reset_controller_dev *rcdev,
return reset_simple_update(rcdev, id, false);
}
static int reset_simple_reset(struct reset_controller_dev *rcdev,
unsigned long id)
{
struct reset_simple_data *data = to_reset_simple_data(rcdev);
int ret;
if (!data->reset_us)
return -ENOTSUPP;
ret = reset_simple_assert(rcdev, id);
if (ret)
return ret;
usleep_range(data->reset_us, data->reset_us * 2);
return reset_simple_deassert(rcdev, id);
}
static int reset_simple_status(struct reset_controller_dev *rcdev,
unsigned long id)
{
......@@ -81,6 +99,7 @@ static int reset_simple_status(struct reset_controller_dev *rcdev,
const struct reset_control_ops reset_simple_ops = {
.assert = reset_simple_assert,
.deassert = reset_simple_deassert,
.reset = reset_simple_reset,
.status = reset_simple_status,
};
EXPORT_SYMBOL_GPL(reset_simple_ops);
......
......@@ -11,13 +11,12 @@
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/reset-controller.h>
#include <linux/reset/reset-simple.h>
#include <linux/reset/socfpga.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include "reset-simple.h"
#define SOCFPGA_NR_BANKS 8
static int a10_reset_init(struct device_node *np)
......
......@@ -14,13 +14,12 @@
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/reset-controller.h>
#include <linux/reset/reset-simple.h>
#include <linux/reset/sunxi.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include "reset-simple.h"
static int sunxi_reset_init(struct device_node *np)
{
struct reset_simple_data *data;
......
......@@ -9,8 +9,7 @@
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include "reset-simple.h"
#include <linux/reset/reset-simple.h>
#define MAX_CLKS 2
#define MAX_RSTS 2
......
/* SPDX-License-Identifier: GPL-2.0+ */
#ifndef __DT_BINDINGS_CLOCK_BCM3368_H
#define __DT_BINDINGS_CLOCK_BCM3368_H
#define BCM3368_CLK_MAC 3
#define BCM3368_CLK_TC 5
#define BCM3368_CLK_US_TOP 6
#define BCM3368_CLK_DS_TOP 7
#define BCM3368_CLK_ACM 8
#define BCM3368_CLK_SPI 9
#define BCM3368_CLK_USBS 10
#define BCM3368_CLK_BMU 11
#define BCM3368_CLK_PCM 12
#define BCM3368_CLK_NTP 13
#define BCM3368_CLK_ACP_B 14
#define BCM3368_CLK_ACP_A 15
#define BCM3368_CLK_EMUSB 17
#define BCM3368_CLK_ENET0 18
#define BCM3368_CLK_ENET1 19
#define BCM3368_CLK_USBSU 20
#define BCM3368_CLK_EPHY 21
#endif /* __DT_BINDINGS_CLOCK_BCM3368_H */
/* SPDX-License-Identifier: GPL-2.0+ */
#ifndef __DT_BINDINGS_CLOCK_BCM6318_H
#define __DT_BINDINGS_CLOCK_BCM6318_H
#define BCM6318_CLK_ADSL_ASB 0
#define BCM6318_CLK_USB_ASB 1
#define BCM6318_CLK_MIPS_ASB 2
#define BCM6318_CLK_PCIE_ASB 3
#define BCM6318_CLK_PHYMIPS_ASB 4
#define BCM6318_CLK_ROBOSW_ASB 5
#define BCM6318_CLK_SAR_ASB 6
#define BCM6318_CLK_SDR_ASB 7
#define BCM6318_CLK_SWREG_ASB 8
#define BCM6318_CLK_PERIPH_ASB 9
#define BCM6318_CLK_CPUBUS160 10
#define BCM6318_CLK_ADSL 11
#define BCM6318_CLK_SAR125 12
#define BCM6318_CLK_MIPS 13
#define BCM6318_CLK_PCIE 14
#define BCM6318_CLK_ROBOSW250 16
#define BCM6318_CLK_ROBOSW025 17
#define BCM6318_CLK_SDR 19
#define BCM6318_CLK_USBD 20
#define BCM6318_CLK_HSSPI 25
#define BCM6318_CLK_PCIE25 27
#define BCM6318_CLK_PHYMIPS 28
#define BCM6318_CLK_AFE 29
#define BCM6318_CLK_QPROC 30
#define BCM6318_UCLK_ADSL 0
#define BCM6318_UCLK_ARB 1
#define BCM6318_UCLK_MIPS 2
#define BCM6318_UCLK_PCIE 3
#define BCM6318_UCLK_PERIPH 4
#define BCM6318_UCLK_PHYMIPS 5
#define BCM6318_UCLK_ROBOSW 6
#define BCM6318_UCLK_SAR 7
#define BCM6318_UCLK_SDR 8
#define BCM6318_UCLK_USB 9
#endif /* __DT_BINDINGS_CLOCK_BCM6318_H */
/* SPDX-License-Identifier: GPL-2.0+ */
#ifndef __DT_BINDINGS_CLOCK_BCM63268_H
#define __DT_BINDINGS_CLOCK_BCM63268_H
#define BCM63268_CLK_DIS_GLESS 0
#define BCM63268_CLK_VDSL_QPROC 1
#define BCM63268_CLK_VDSL_AFE 2
#define BCM63268_CLK_VDSL 3
#define BCM63268_CLK_MIPS 4
#define BCM63268_CLK_WLAN_OCP 5
#define BCM63268_CLK_DECT 6
#define BCM63268_CLK_FAP0 7
#define BCM63268_CLK_FAP1 8
#define BCM63268_CLK_SAR 9
#define BCM63268_CLK_ROBOSW 10
#define BCM63268_CLK_PCM 11
#define BCM63268_CLK_USBD 12
#define BCM63268_CLK_USBH 13
#define BCM63268_CLK_IPSEC 14
#define BCM63268_CLK_SPI 15
#define BCM63268_CLK_HSSPI 16
#define BCM63268_CLK_PCIE 17
#define BCM63268_CLK_PHYMIPS 18
#define BCM63268_CLK_GMAC 19
#define BCM63268_CLK_NAND 20
#define BCM63268_CLK_TBUS 27
#define BCM63268_CLK_ROBOSW250 31
#endif /* __DT_BINDINGS_CLOCK_BCM63268_H */
/* SPDX-License-Identifier: GPL-2.0+ */
#ifndef __DT_BINDINGS_CLOCK_BCM6328_H
#define __DT_BINDINGS_CLOCK_BCM6328_H
#define BCM6328_CLK_PHYMIPS 0
#define BCM6328_CLK_ADSL_QPROC 1
#define BCM6328_CLK_ADSL_AFE 2
#define BCM6328_CLK_ADSL 3
#define BCM6328_CLK_MIPS 4
#define BCM6328_CLK_SAR 5
#define BCM6328_CLK_PCM 6
#define BCM6328_CLK_USBD 7
#define BCM6328_CLK_USBH 8
#define BCM6328_CLK_HSSPI 9
#define BCM6328_CLK_PCIE 10
#define BCM6328_CLK_ROBOSW 11
#endif /* __DT_BINDINGS_CLOCK_BCM6328_H */
/* SPDX-License-Identifier: GPL-2.0+ */
#ifndef __DT_BINDINGS_CLOCK_BCM6358_H
#define __DT_BINDINGS_CLOCK_BCM6358_H
#define BCM6358_CLK_ENET 4
#define BCM6358_CLK_ADSLPHY 5
#define BCM6358_CLK_PCM 8
#define BCM6358_CLK_SPI 9
#define BCM6358_CLK_USBS 10
#define BCM6358_CLK_SAR 11
#define BCM6358_CLK_EMUSB 17
#define BCM6358_CLK_ENET0 18
#define BCM6358_CLK_ENET1 19
#define BCM6358_CLK_USBSU 20
#define BCM6358_CLK_EPHY 21
#endif /* __DT_BINDINGS_CLOCK_BCM6358_H */
/* SPDX-License-Identifier: GPL-2.0+ */
#ifndef __DT_BINDINGS_CLOCK_BCM6362_H
#define __DT_BINDINGS_CLOCK_BCM6362_H
#define BCM6362_CLK_ADSL_QPROC 1
#define BCM6362_CLK_ADSL_AFE 2
#define BCM6362_CLK_ADSL 3
#define BCM6362_CLK_MIPS 4
#define BCM6362_CLK_WLAN_OCP 5
#define BCM6362_CLK_SWPKT_USB 7
#define BCM6362_CLK_SWPKT_SAR 8
#define BCM6362_CLK_SAR 9
#define BCM6362_CLK_ROBOSW 10
#define BCM6362_CLK_PCM 11
#define BCM6362_CLK_USBD 12
#define BCM6362_CLK_USBH 13
#define BCM6362_CLK_IPSEC 14
#define BCM6362_CLK_SPI 15
#define BCM6362_CLK_HSSPI 16
#define BCM6362_CLK_PCIE 17
#define BCM6362_CLK_FAP 18
#define BCM6362_CLK_PHYMIPS 19
#define BCM6362_CLK_NAND 20
#endif /* __DT_BINDINGS_CLOCK_BCM6362_H */
/* SPDX-License-Identifier: GPL-2.0+ */
#ifndef __DT_BINDINGS_CLOCK_BCM6368_H
#define __DT_BINDINGS_CLOCK_BCM6368_H
#define BCM6368_CLK_VDSL_QPROC 2
#define BCM6368_CLK_VDSL_AFE 3
#define BCM6368_CLK_VDSL_BONDING 4
#define BCM6368_CLK_VDSL 5
#define BCM6368_CLK_PHYMIPS 6
#define BCM6368_CLK_SWPKT_USB 7
#define BCM6368_CLK_SWPKT_SAR 8
#define BCM6368_CLK_SPI 9
#define BCM6368_CLK_USBD 10
#define BCM6368_CLK_SAR 11
#define BCM6368_CLK_ROBOSW 12
#define BCM6368_CLK_UTOPIA 13
#define BCM6368_CLK_PCM 14
#define BCM6368_CLK_USBH 15
#define BCM6368_CLK_DIS_GLESS 16
#define BCM6368_CLK_NAND 17
#define BCM6368_CLK_IPSEC 18
#endif /* __DT_BINDINGS_CLOCK_BCM6368_H */
......@@ -27,6 +27,12 @@
* @status_active_low: if true, bits read back as cleared while the reset is
* asserted. Otherwise, bits read back as set while the
* reset is asserted.
* @reset_us: Minimum delay in microseconds needed that needs to be
* waited for between an assert and a deassert to reset the
* device. If multiple consumers with different delay
* requirements are connected to this controller, it must
* be the largest minimum delay. 0 means that such a delay is
* unknown and the reset operation is unsupported.
*/
struct reset_simple_data {
spinlock_t lock;
......@@ -34,6 +40,7 @@ struct reset_simple_data {
struct reset_controller_dev rcdev;
bool active_low;
bool status_active_low;
unsigned int reset_us;
};
extern const struct reset_control_ops reset_simple_ops;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment