diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2024-03-19 11:57:26 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2024-03-19 11:57:26 -0700 |
commit | 78c3925c048c752334873f56c3a3d1c9d53e0416 (patch) | |
tree | 8f07adc33ca2c14a1915988c838db6867038b1a1 /drivers | |
parent | f9c035492f2010e1e7aede1f1bd32181d7cef2dc (diff) | |
parent | 72ebb41b88f9d7c10c5e159e0507074af0a22fe2 (diff) |
Merge tag 'soc-late-6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc
Pull more ARM SoC updates from Arnd Bergmann:
"These are changes that for some reason ended up not making it into the
first four branches but that should still make it into 6.9:
- A rework of the omap clock support that touches both drivers and
device tree files
- The reset controller branch changes that had a dependency on late
bugfixes. Merging them here avoids a backmerge of 6.8-rc5 into the
drivers branch
- The RISC-V/starfive, RISC-V/microchip and ARM/Broadcom devicetree
changes that got delayed and needed some extra time in linux-next
for wider testing"
* tag 'soc-late-6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (31 commits)
soc: fsl: dpio: fix kcalloc() argument order
bus: ts-nbus: Improve error reporting
bus: ts-nbus: Convert to atomic pwm API
riscv: dts: starfive: jh7110: Add camera subsystem nodes
ARM: bcm: stop selecing CONFIG_TICK_ONESHOT
ARM: dts: omap3: Update clksel clocks to use reg instead of ti,bit-shift
ARM: dts: am3: Update clksel clocks to use reg instead of ti,bit-shift
clk: ti: Improve clksel clock bit parsing for reg property
clk: ti: Handle possible address in the node name
dt-bindings: pwm: opencores: Add compatible for StarFive JH8100
dt-bindings: riscv: cpus: reg matches hart ID
reset: Instantiate reset GPIO controller for shared reset-gpios
reset: gpio: Add GPIO-based reset controller
cpufreq: do not open-code of_phandle_args_equal()
of: Add of_phandle_args_equal() helper
reset: simple: add support for Sophgo SG2042
dt-bindings: reset: sophgo: support SG2042
riscv: dts: microchip: add specific compatible for mpfs pdma
riscv: dts: microchip: add missing CAN bus clocks
ARM: brcmstb: Add debug UART entry for 74165
...
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/bus/brcmstb_gisb.c | 15 | ||||
-rw-r--r-- | drivers/bus/ts-nbus.c | 81 | ||||
-rw-r--r-- | drivers/clk/ti/apll.c | 11 | ||||
-rw-r--r-- | drivers/clk/ti/clk.c | 71 | ||||
-rw-r--r-- | drivers/clk/ti/clock.h | 1 | ||||
-rw-r--r-- | drivers/clk/ti/divider.c | 5 | ||||
-rw-r--r-- | drivers/clk/ti/gate.c | 9 | ||||
-rw-r--r-- | drivers/clk/ti/interface.c | 4 | ||||
-rw-r--r-- | drivers/clk/ti/mux.c | 6 | ||||
-rw-r--r-- | drivers/reset/Kconfig | 12 | ||||
-rw-r--r-- | drivers/reset/Makefile | 1 | ||||
-rw-r--r-- | drivers/reset/core.c | 224 | ||||
-rw-r--r-- | drivers/reset/reset-gpio.c | 119 | ||||
-rw-r--r-- | drivers/reset/reset-simple.c | 2 | ||||
-rw-r--r-- | drivers/soc/fsl/dpio/dpio-service.c | 2 |
15 files changed, 464 insertions, 99 deletions
diff --git a/drivers/bus/brcmstb_gisb.c b/drivers/bus/brcmstb_gisb.c index b6dfe4340da2..65ae758f3194 100644 --- a/drivers/bus/brcmstb_gisb.c +++ b/drivers/bus/brcmstb_gisb.c @@ -96,6 +96,20 @@ static const int gisb_offsets_bcm7400[] = { [ARB_ERR_CAP_MASTER] = 0x0d8, }; +static const int gisb_offsets_bcm74165[] = { + [ARB_TIMER] = 0x008, + [ARB_BP_CAP_CLR] = 0x044, + [ARB_BP_CAP_HI_ADDR] = -1, + [ARB_BP_CAP_ADDR] = 0x048, + [ARB_BP_CAP_STATUS] = 0x058, + [ARB_BP_CAP_MASTER] = 0x05c, + [ARB_ERR_CAP_CLR] = 0x038, + [ARB_ERR_CAP_HI_ADDR] = -1, + [ARB_ERR_CAP_ADDR] = 0x020, + [ARB_ERR_CAP_STATUS] = 0x030, + [ARB_ERR_CAP_MASTER] = 0x034, +}; + static const int gisb_offsets_bcm7435[] = { [ARB_TIMER] = 0x00c, [ARB_BP_CAP_CLR] = 0x014, @@ -393,6 +407,7 @@ static const struct of_device_id brcmstb_gisb_arb_of_match[] = { { .compatible = "brcm,bcm7400-gisb-arb", .data = gisb_offsets_bcm7400 }, { .compatible = "brcm,bcm7278-gisb-arb", .data = gisb_offsets_bcm7278 }, { .compatible = "brcm,bcm7038-gisb-arb", .data = gisb_offsets_bcm7038 }, + { .compatible = "brcm,bcm74165-gisb-arb", .data = gisb_offsets_bcm74165 }, { }, }; diff --git a/drivers/bus/ts-nbus.c b/drivers/bus/ts-nbus.c index 4fa932cb0915..baf22a82c47a 100644 --- a/drivers/bus/ts-nbus.c +++ b/drivers/bus/ts-nbus.c @@ -39,45 +39,39 @@ struct ts_nbus { /* * request all gpios required by the bus. */ -static int ts_nbus_init_pdata(struct platform_device *pdev, struct ts_nbus - *ts_nbus) +static int ts_nbus_init_pdata(struct platform_device *pdev, + struct ts_nbus *ts_nbus) { ts_nbus->data = devm_gpiod_get_array(&pdev->dev, "ts,data", GPIOD_OUT_HIGH); - if (IS_ERR(ts_nbus->data)) { - dev_err(&pdev->dev, "failed to retrieve ts,data-gpio from dts\n"); - return PTR_ERR(ts_nbus->data); - } + if (IS_ERR(ts_nbus->data)) + return dev_err_probe(&pdev->dev, PTR_ERR(ts_nbus->data), + "failed to retrieve ts,data-gpio from dts\n"); ts_nbus->csn = devm_gpiod_get(&pdev->dev, "ts,csn", GPIOD_OUT_HIGH); - if (IS_ERR(ts_nbus->csn)) { - dev_err(&pdev->dev, "failed to retrieve ts,csn-gpio from dts\n"); - return PTR_ERR(ts_nbus->csn); - } + if (IS_ERR(ts_nbus->csn)) + return dev_err_probe(&pdev->dev, PTR_ERR(ts_nbus->csn), + "failed to retrieve ts,csn-gpio from dts\n"); ts_nbus->txrx = devm_gpiod_get(&pdev->dev, "ts,txrx", GPIOD_OUT_HIGH); - if (IS_ERR(ts_nbus->txrx)) { - dev_err(&pdev->dev, "failed to retrieve ts,txrx-gpio from dts\n"); - return PTR_ERR(ts_nbus->txrx); - } + if (IS_ERR(ts_nbus->txrx)) + return dev_err_probe(&pdev->dev, PTR_ERR(ts_nbus->txrx), + "failed to retrieve ts,txrx-gpio from dts\n"); ts_nbus->strobe = devm_gpiod_get(&pdev->dev, "ts,strobe", GPIOD_OUT_HIGH); - if (IS_ERR(ts_nbus->strobe)) { - dev_err(&pdev->dev, "failed to retrieve ts,strobe-gpio from dts\n"); - return PTR_ERR(ts_nbus->strobe); - } + if (IS_ERR(ts_nbus->strobe)) + return dev_err_probe(&pdev->dev, PTR_ERR(ts_nbus->strobe), + "failed to retrieve ts,strobe-gpio from dts\n"); ts_nbus->ale = devm_gpiod_get(&pdev->dev, "ts,ale", GPIOD_OUT_HIGH); - if (IS_ERR(ts_nbus->ale)) { - dev_err(&pdev->dev, "failed to retrieve ts,ale-gpio from dts\n"); - return PTR_ERR(ts_nbus->ale); - } + if (IS_ERR(ts_nbus->ale)) + return dev_err_probe(&pdev->dev, PTR_ERR(ts_nbus->ale), + "failed to retrieve ts,ale-gpio from dts\n"); ts_nbus->rdy = devm_gpiod_get(&pdev->dev, "ts,rdy", GPIOD_IN); - if (IS_ERR(ts_nbus->rdy)) { - dev_err(&pdev->dev, "failed to retrieve ts,rdy-gpio from dts\n"); - return PTR_ERR(ts_nbus->rdy); - } + if (IS_ERR(ts_nbus->rdy)) + return dev_err_probe(&pdev->dev, PTR_ERR(ts_nbus->rdy), + "failed to retrieve ts,rdy-gpio from dts\n"); return 0; } @@ -273,7 +267,7 @@ EXPORT_SYMBOL_GPL(ts_nbus_write); static int ts_nbus_probe(struct platform_device *pdev) { struct pwm_device *pwm; - struct pwm_args pargs; + struct pwm_state state; struct device *dev = &pdev->dev; struct ts_nbus *ts_nbus; int ret; @@ -289,32 +283,24 @@ static int ts_nbus_probe(struct platform_device *pdev) return ret; pwm = devm_pwm_get(dev, NULL); - if (IS_ERR(pwm)) { - ret = PTR_ERR(pwm); - if (ret != -EPROBE_DEFER) - dev_err(dev, "unable to request PWM\n"); - return ret; - } + if (IS_ERR(pwm)) + return dev_err_probe(dev, PTR_ERR(pwm), + "unable to request PWM\n"); - pwm_get_args(pwm, &pargs); - if (!pargs.period) { - dev_err(&pdev->dev, "invalid PWM period\n"); - return -EINVAL; - } + pwm_init_state(pwm, &state); + if (!state.period) + return dev_err_probe(dev, -EINVAL, "invalid PWM period\n"); - /* - * FIXME: pwm_apply_args() should be removed when switching to - * the atomic PWM API. - */ - pwm_apply_args(pwm); - ret = pwm_config(pwm, pargs.period, pargs.period); + state.duty_cycle = state.period; + state.enabled = true; + + ret = pwm_apply_state(pwm, &state); if (ret < 0) - return ret; + return dev_err_probe(dev, ret, "failed to configure PWM\n"); /* * we can now start the FPGA and populate the peripherals. */ - pwm_enable(pwm); ts_nbus->pwm = pwm; /* @@ -324,7 +310,8 @@ static int ts_nbus_probe(struct platform_device *pdev) ret = of_platform_populate(dev->of_node, NULL, NULL, dev); if (ret < 0) - return ret; + return dev_err_probe(dev, ret, + "failed to populate platform devices on bus\n"); dev_info(dev, "initialized\n"); diff --git a/drivers/clk/ti/apll.c b/drivers/clk/ti/apll.c index 93183287c58d..43514e6f3b78 100644 --- a/drivers/clk/ti/apll.c +++ b/drivers/clk/ti/apll.c @@ -376,14 +376,9 @@ static void __init of_omap2_apll_setup(struct device_node *node) } clk_hw->fixed_rate = val; - if (of_property_read_u32(node, "ti,bit-shift", &val)) { - pr_err("%pOFn missing bit-shift\n", node); - goto cleanup; - } - - clk_hw->enable_bit = val; - ad->enable_mask = 0x3 << val; - ad->autoidle_mask = 0x3 << val; + clk_hw->enable_bit = ti_clk_get_legacy_bit_shift(node); + ad->enable_mask = 0x3 << clk_hw->enable_bit; + ad->autoidle_mask = 0x3 << clk_hw->enable_bit; if (of_property_read_u32(node, "ti,idlest-shift", &val)) { pr_err("%pOFn missing idlest-shift\n", node); diff --git a/drivers/clk/ti/clk.c b/drivers/clk/ti/clk.c index 1862958ab412..f2117fef7c7d 100644 --- a/drivers/clk/ti/clk.c +++ b/drivers/clk/ti/clk.c @@ -7,6 +7,7 @@ * Tero Kristo <t-kristo@ti.com> */ +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/clkdev.h> @@ -15,6 +16,7 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/list.h> +#include <linux/minmax.h> #include <linux/regmap.h> #include <linux/string_helpers.h> #include <linux/memblock.h> @@ -114,20 +116,26 @@ int ti_clk_setup_ll_ops(struct ti_clk_ll_ops *ops) /* * Eventually we could standardize to using '_' for clk-*.c files to follow the - * TRM naming and leave out the tmp name here. + * TRM naming. */ static struct device_node *ti_find_clock_provider(struct device_node *from, const char *name) { + char *tmp __free(kfree) = NULL; struct device_node *np; bool found = false; const char *n; - char *tmp; + char *p; tmp = kstrdup_and_replace(name, '-', '_', GFP_KERNEL); if (!tmp) return NULL; + /* Ignore a possible address for the node name */ + p = strchr(tmp, '@'); + if (p) + *p = '\0'; + /* Node named "clock" with "clock-output-names" */ for_each_of_allnodes_from(from, np) { if (of_property_read_string_index(np, "clock-output-names", @@ -140,7 +148,6 @@ static struct device_node *ti_find_clock_provider(struct device_node *from, break; } } - kfree(tmp); if (found) { of_node_put(from); @@ -148,7 +155,7 @@ static struct device_node *ti_find_clock_provider(struct device_node *from, } /* Fall back to using old node name base provider name */ - return of_find_node_by_name(from, name); + return of_find_node_by_name(from, tmp); } /** @@ -301,8 +308,9 @@ int __init ti_clk_retry_init(struct device_node *node, void *user, int ti_clk_get_reg_addr(struct device_node *node, int index, struct clk_omap_reg *reg) { - u32 val; - int i; + u32 clksel_addr, val; + bool is_clksel = false; + int i, err; for (i = 0; i < CLK_MAX_MEMMAPS; i++) { if (clocks_node_ptr[i] == node->parent) @@ -318,21 +326,62 @@ int ti_clk_get_reg_addr(struct device_node *node, int index, reg->index = i; - if (of_property_read_u32_index(node, "reg", index, &val)) { - if (of_property_read_u32_index(node->parent, "reg", - index, &val)) { - pr_err("%pOFn or parent must have reg[%d]!\n", - node, index); + if (of_device_is_compatible(node->parent, "ti,clksel")) { + err = of_property_read_u32_index(node->parent, "reg", index, &clksel_addr); + if (err) { + pr_err("%pOFn parent clksel must have reg[%d]!\n", node, index); return -EINVAL; } + is_clksel = true; } + err = of_property_read_u32_index(node, "reg", index, &val); + if (err && is_clksel) { + /* Legacy clksel with no reg and a possible ti,bit-shift property */ + reg->offset = clksel_addr; + reg->bit = ti_clk_get_legacy_bit_shift(node); + reg->ptr = NULL; + + return 0; + } + + /* Updated clksel clock with a proper reg property */ + if (is_clksel) { + reg->offset = clksel_addr; + reg->bit = val; + reg->ptr = NULL; + return 0; + } + + /* Other clocks that may or may not have ti,bit-shift property */ reg->offset = val; + reg->bit = ti_clk_get_legacy_bit_shift(node); reg->ptr = NULL; return 0; } +/** + * ti_clk_get_legacy_bit_shift - get bit shift for a clock register + * @node: device node for the clock + * + * Gets the clock register bit shift using the legacy ti,bit-shift + * property. Only needed for legacy clock, and can be eventually + * dropped once all the composite clocks use a clksel node with a + * proper reg property. + */ +int ti_clk_get_legacy_bit_shift(struct device_node *node) +{ + int err; + u32 val; + + err = of_property_read_u32(node, "ti,bit-shift", &val); + if (!err && in_range(val, 0, 32)) + return val; + + return 0; +} + void ti_clk_latch(struct clk_omap_reg *reg, s8 shift) { u32 latch; diff --git a/drivers/clk/ti/clock.h b/drivers/clk/ti/clock.h index 16a9f7c2280a..2de7acea1ea0 100644 --- a/drivers/clk/ti/clock.h +++ b/drivers/clk/ti/clock.h @@ -216,6 +216,7 @@ int ti_clk_parse_divider_data(int *div_table, int num_dividers, int max_div, int ti_clk_get_reg_addr(struct device_node *node, int index, struct clk_omap_reg *reg); +int ti_clk_get_legacy_bit_shift(struct device_node *node); void ti_dt_clocks_register(struct ti_dt_clk *oclks); int ti_clk_retry_init(struct device_node *node, void *user, ti_of_clk_init_cb_t func); diff --git a/drivers/clk/ti/divider.c b/drivers/clk/ti/divider.c index 5d5bb123ba94..ade99ab6cfa9 100644 --- a/drivers/clk/ti/divider.c +++ b/drivers/clk/ti/divider.c @@ -477,10 +477,7 @@ static int __init ti_clk_divider_populate(struct device_node *node, if (ret) return ret; - if (!of_property_read_u32(node, "ti,bit-shift", &val)) - div->shift = val; - else - div->shift = 0; + div->shift = div->reg.bit; if (!of_property_read_u32(node, "ti,latch-bit", &val)) div->latch = val; diff --git a/drivers/clk/ti/gate.c b/drivers/clk/ti/gate.c index 8e477d50d0fd..a9febd6356b8 100644 --- a/drivers/clk/ti/gate.c +++ b/drivers/clk/ti/gate.c @@ -132,7 +132,6 @@ static void __init _of_ti_gate_clk_setup(struct device_node *node, struct clk_omap_reg reg; const char *name; u8 enable_bit = 0; - u32 val; u32 flags = 0; u8 clk_gate_flags = 0; @@ -140,8 +139,7 @@ static void __init _of_ti_gate_clk_setup(struct device_node *node, if (ti_clk_get_reg_addr(node, 0, ®)) return; - if (!of_property_read_u32(node, "ti,bit-shift", &val)) - enable_bit = val; + enable_bit = reg.bit; } if (of_clk_get_parent_count(node) != 1) { @@ -170,7 +168,6 @@ _of_ti_composite_gate_clk_setup(struct device_node *node, const struct clk_hw_omap_ops *hw_ops) { struct clk_hw_omap *gate; - u32 val = 0; gate = kzalloc(sizeof(*gate), GFP_KERNEL); if (!gate) @@ -179,9 +176,7 @@ _of_ti_composite_gate_clk_setup(struct device_node *node, if (ti_clk_get_reg_addr(node, 0, &gate->enable_reg)) goto cleanup; - of_property_read_u32(node, "ti,bit-shift", &val); - - gate->enable_bit = val; + gate->enable_bit = gate->enable_reg.bit; gate->ops = hw_ops; if (!ti_clk_add_component(node, &gate->hw, CLK_COMPONENT_TYPE_GATE)) diff --git a/drivers/clk/ti/interface.c b/drivers/clk/ti/interface.c index 172301c646f8..3eb35c87c0ed 100644 --- a/drivers/clk/ti/interface.c +++ b/drivers/clk/ti/interface.c @@ -66,13 +66,11 @@ static void __init _of_ti_interface_clk_setup(struct device_node *node, struct clk_omap_reg reg; u8 enable_bit = 0; const char *name; - u32 val; if (ti_clk_get_reg_addr(node, 0, ®)) return; - if (!of_property_read_u32(node, "ti,bit-shift", &val)) - enable_bit = val; + enable_bit = reg.bit; parent_name = of_clk_get_parent_name(node, 0); if (!parent_name) { diff --git a/drivers/clk/ti/mux.c b/drivers/clk/ti/mux.c index 1ebafa386be6..216d85d6aac6 100644 --- a/drivers/clk/ti/mux.c +++ b/drivers/clk/ti/mux.c @@ -189,7 +189,7 @@ static void of_mux_clk_setup(struct device_node *node) if (ti_clk_get_reg_addr(node, 0, ®)) goto cleanup; - of_property_read_u32(node, "ti,bit-shift", &shift); + shift = reg.bit; of_property_read_u32(node, "ti,latch-bit", &latch); @@ -252,7 +252,6 @@ static void __init of_ti_composite_mux_clk_setup(struct device_node *node) { struct clk_omap_mux *mux; unsigned int num_parents; - u32 val; mux = kzalloc(sizeof(*mux), GFP_KERNEL); if (!mux) @@ -261,8 +260,7 @@ static void __init of_ti_composite_mux_clk_setup(struct device_node *node) if (ti_clk_get_reg_addr(node, 0, &mux->reg)) goto cleanup; - if (!of_property_read_u32(node, "ti,bit-shift", &val)) - mux->shift = val; + mux->shift = mux->reg.bit; if (of_property_read_bool(node, "ti,index-starts-at-one")) mux->flags |= CLK_MUX_INDEX_ONE; diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index ccd59ddd7610..85b27c42cf65 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -66,6 +66,15 @@ config RESET_BRCMSTB_RESCAL This enables the RESCAL reset controller for SATA, PCIe0, or PCIe1 on BCM7216. +config RESET_GPIO + tristate "GPIO reset controller" + help + This enables a generic reset controller for resets attached via + GPIOs. Typically for OF platforms this driver expects "reset-gpios" + property. + + If compiled as module, it will be called reset-gpio. + config RESET_HSDK bool "Synopsys HSDK Reset Driver" depends on HAS_IOMEM @@ -213,7 +222,7 @@ config RESET_SCMI config RESET_SIMPLE bool "Simple Reset Controller Driver" if COMPILE_TEST || EXPERT - default ARCH_ASPEED || ARCH_BCMBCA || ARCH_BITMAIN || ARCH_REALTEK || ARCH_STM32 || (ARCH_INTEL_SOCFPGA && ARM64) || ARCH_SUNXI || ARC + default ARCH_ASPEED || ARCH_BCMBCA || ARCH_BITMAIN || ARCH_REALTEK || ARCH_SOPHGO || ARCH_STM32 || (ARCH_INTEL_SOCFPGA && ARM64) || ARCH_SUNXI || ARC depends on HAS_IOMEM help This enables a simple reset controller driver for reset lines that @@ -228,6 +237,7 @@ config RESET_SIMPLE - RCC reset controller in STM32 MCUs - Allwinner SoCs - SiFive FU740 SoCs + - Sophgo SoCs config RESET_SOCFPGA bool "SoCFPGA Reset Driver" if COMPILE_TEST && (!ARM || !ARCH_INTEL_SOCFPGA) diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 8270da8a4baa..fd8b49fa46fc 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_RESET_BCM6345) += reset-bcm6345.o obj-$(CONFIG_RESET_BERLIN) += reset-berlin.o obj-$(CONFIG_RESET_BRCMSTB) += reset-brcmstb.o obj-$(CONFIG_RESET_BRCMSTB_RESCAL) += reset-brcmstb-rescal.o +obj-$(CONFIG_RESET_GPIO) += reset-gpio.o obj-$(CONFIG_RESET_HSDK) += reset-hsdk.o obj-$(CONFIG_RESET_IMX7) += reset-imx7.o obj-$(CONFIG_RESET_INTEL_GW) += reset-intel-gw.o diff --git a/drivers/reset/core.c b/drivers/reset/core.c index 4d5a78d3c085..dba74e857be6 100644 --- a/drivers/reset/core.c +++ b/drivers/reset/core.c @@ -5,14 +5,19 @@ * Copyright 2013 Philipp Zabel, Pengutronix */ #include <linux/atomic.h> +#include <linux/cleanup.h> #include <linux/device.h> #include <linux/err.h> #include <linux/export.h> #include <linux/kernel.h> #include <linux/kref.h> +#include <linux/gpio/driver.h> +#include <linux/gpio/machine.h> +#include <linux/idr.h> #include <linux/module.h> #include <linux/of.h> #include <linux/acpi.h> +#include <linux/platform_device.h> #include <linux/reset.h> #include <linux/reset-controller.h> #include <linux/slab.h> @@ -23,6 +28,11 @@ static LIST_HEAD(reset_controller_list); static DEFINE_MUTEX(reset_lookup_mutex); static LIST_HEAD(reset_lookup_list); +/* Protects reset_gpio_lookup_list */ +static DEFINE_MUTEX(reset_gpio_lookup_mutex); +static LIST_HEAD(reset_gpio_lookup_list); +static DEFINE_IDA(reset_gpio_ida); + /** * struct reset_control - a reset control * @rcdev: a pointer to the reset controller device @@ -63,6 +73,16 @@ struct reset_control_array { struct reset_control *rstc[] __counted_by(num_rstcs); }; +/** + * struct reset_gpio_lookup - lookup key for ad-hoc created reset-gpio devices + * @of_args: phandle to the reset controller with all the args like GPIO number + * @list: list entry for the reset_gpio_lookup_list + */ +struct reset_gpio_lookup { + struct of_phandle_args of_args; + struct list_head list; +}; + static const char *rcdev_name(struct reset_controller_dev *rcdev) { if (rcdev->dev) @@ -71,6 +91,9 @@ static const char *rcdev_name(struct reset_controller_dev *rcdev) if (rcdev->of_node) return rcdev->of_node->full_name; + if (rcdev->of_args) + return rcdev->of_args->np->full_name; + return NULL; } @@ -99,6 +122,9 @@ static int of_reset_simple_xlate(struct reset_controller_dev *rcdev, */ int reset_controller_register(struct reset_controller_dev *rcdev) { + if (rcdev->of_node && rcdev->of_args) + return -EINVAL; + if (!rcdev->of_xlate) { rcdev->of_reset_n_cells = 1; rcdev->of_xlate = of_reset_simple_xlate; @@ -813,12 +839,171 @@ static void __reset_control_put_internal(struct reset_control *rstc) kref_put(&rstc->refcnt, __reset_control_release); } +static int __reset_add_reset_gpio_lookup(int id, struct device_node *np, + unsigned int gpio, + unsigned int of_flags) +{ + const struct fwnode_handle *fwnode = of_fwnode_handle(np); + unsigned int lookup_flags; + const char *label_tmp; + + /* + * Later we map GPIO flags between OF and Linux, however not all + * constants from include/dt-bindings/gpio/gpio.h and + * include/linux/gpio/machine.h match each other. + */ + if (of_flags > GPIO_ACTIVE_LOW) { + pr_err("reset-gpio code does not support GPIO flags %u for GPIO %u\n", + of_flags, gpio); + return -EINVAL; + } + + struct gpio_device *gdev __free(gpio_device_put) = gpio_device_find_by_fwnode(fwnode); + if (!gdev) + return -EPROBE_DEFER; + + label_tmp = gpio_device_get_label(gdev); + if (!label_tmp) + return -EINVAL; + + char *label __free(kfree) = kstrdup(label_tmp, GFP_KERNEL); + if (!label) + return -ENOMEM; + + /* Size: one lookup entry plus sentinel */ + struct gpiod_lookup_table *lookup __free(kfree) = kzalloc(struct_size(lookup, table, 2), + GFP_KERNEL); + if (!lookup) + return -ENOMEM; + + lookup->dev_id = kasprintf(GFP_KERNEL, "reset-gpio.%d", id); + if (!lookup->dev_id) + return -ENOMEM; + + lookup_flags = GPIO_PERSISTENT; + lookup_flags |= of_flags & GPIO_ACTIVE_LOW; + lookup->table[0] = GPIO_LOOKUP(no_free_ptr(label), gpio, "reset", + lookup_flags); + + /* Not freed on success, because it is persisent subsystem data. */ + gpiod_add_lookup_table(no_free_ptr(lookup)); + + return 0; +} + +/* + * @args: phandle to the GPIO provider with all the args like GPIO number + */ +static int __reset_add_reset_gpio_device(const struct of_phandle_args *args) +{ + struct reset_gpio_lookup *rgpio_dev; + struct platform_device *pdev; + int id, ret; + + /* + * Currently only #gpio-cells=2 is supported with the meaning of: + * args[0]: GPIO number + * args[1]: GPIO flags + * TODO: Handle other cases. + */ + if (args->args_count != 2) + return -ENOENT; + + /* + * Registering reset-gpio device might cause immediate + * bind, resulting in its probe() registering new reset controller thus + * taking reset_list_mutex lock via reset_controller_register(). + */ + lockdep_assert_not_held(&reset_list_mutex); + + mutex_lock(&reset_gpio_lookup_mutex); + + list_for_each_entry(rgpio_dev, &reset_gpio_lookup_list, list) { + if (args->np == rgpio_dev->of_args.np) { + if (of_phandle_args_equal(args, &rgpio_dev->of_args)) + goto out; /* Already on the list, done */ + } + } + + id = ida_alloc(&reset_gpio_ida, GFP_KERNEL); + if (id < 0) { + ret = id; + goto err_unlock; + } + + /* Not freed on success, because it is persisent subsystem data. */ + rgpio_dev = kzalloc(sizeof(*rgpio_dev), GFP_KERNEL); + if (!rgpio_dev) { + ret = -ENOMEM; + goto err_ida_free; + } + + ret = __reset_add_reset_gpio_lookup(id, args->np, args->args[0], + args->args[1]); + if (ret < 0) + goto err_kfree; + + rgpio_dev->of_args = *args; + /* + * We keep the device_node reference, but of_args.np is put at the end + * of __of_reset_control_get(), so get it one more time. + * Hold reference as long as rgpio_dev memory is valid. + */ + of_node_get(rgpio_dev->of_args.np); + pdev = platform_device_register_data(NULL, "reset-gpio", id, + &rgpio_dev->of_args, + sizeof(rgpio_dev->of_args)); + ret = PTR_ERR_OR_ZERO(pdev); + if (ret) + goto err_put; + + list_add(&rgpio_dev->list, &reset_gpio_lookup_list); + +out: + mutex_unlock(&reset_gpio_lookup_mutex); + + return 0; + +err_put: + of_node_put(rgpio_dev->of_args.np); +err_kfree: + kfree(rgpio_dev); +err_ida_free: + ida_free(&reset_gpio_ida, id); +err_unlock: + mutex_unlock(&reset_gpio_lookup_mutex); + + return ret; +} + +static struct reset_controller_dev *__reset_find_rcdev(const struct of_phandle_args *args, + bool gpio_fallback) +{ + struct reset_controller_dev *rcdev; + + lockdep_assert_held(&reset_list_mutex); + + list_for_each_entry(rcdev, &reset_controller_list, list) { + if (gpio_fallback) { + if (rcdev->of_args && of_phandle_args_equal(args, + rcdev->of_args)) + return rcdev; + } else { + if (args->np == rcdev->of_node) + return rcdev; + } + } + + return NULL; +} + struct reset_control * __of_reset_control_get(struct device_node *node, const char *id, int index, bool shared, bool optional, bool acquired) { + bool gpio_fallback = false; struct reset_control *rstc; - struct reset_controller_dev *r, *rcdev; + struct reset_controller_dev *rcdev; struct of_phandle_args args; int rstc_id; int ret; @@ -839,39 +1024,52 @@ __of_reset_control_get(struct device_node *node, const char *id, int index, index, &args); if (ret == -EINVAL) return ERR_PTR(ret); - if (ret) - return optional ? NULL : ERR_PTR(ret); + if (ret) { + if (!IS_ENABLED(CONFIG_RESET_GPIO)) + return optional ? NULL : ERR_PTR(ret); - mutex_lock(&reset_list_mutex); - rcdev = NULL; - list_for_each_entry(r, &reset_controller_list, list) { - if (args.np == r->of_node) { - rcdev = r; - break; + /* + * There can be only one reset-gpio for regular devices, so + * don't bother with the "reset-gpios" phandle index. + */ + ret = of_parse_phandle_with_args(node, "reset-gpios", "#gpio-cells", + 0, &args); + if (ret) + return optional ? NULL : ERR_PTR(ret); + + gpio_fallback = true; + + ret = __reset_add_reset_gpio_device(&args); + if (ret) { + rstc = ERR_PTR(ret); + goto out_put; } } + mutex_lock(&reset_list_mutex); + rcdev = __reset_find_rcdev(&args, gpio_fallback); if (!rcdev) { rstc = ERR_PTR(-EPROBE_DEFER); - goto out; + goto out_unlock; } if (WARN_ON(args.args_count != rcdev->of_reset_n_cells)) { rstc = ERR_PTR(-EINVAL); - goto out; + goto out_unlock; } rstc_id = rcdev->of_xlate(rcdev, &args); if (rstc_id < 0) { rstc = ERR_PTR(rstc_id); - goto out; + goto out_unlock; } /* reset_list_mutex also protects the rcdev's reset_control list */ rstc = __reset_control_get_internal(rcdev, rstc_id, shared, acquired); -out: +out_unlock: mutex_unlock(&reset_list_mutex); +out_put: of_node_put(args.np); return rstc; diff --git a/drivers/reset/reset-gpio.c b/drivers/reset/reset-gpio.c new file mode 100644 index 000000000000..2290b25b6703 --- /dev/null +++ b/drivers/reset/reset-gpio.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/gpio/consumer.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/reset-controller.h> + +struct reset_gpio_priv { + struct reset_controller_dev rc; + struct gpio_desc *reset; +}; + +static inline struct reset_gpio_priv +*rc_to_reset_gpio(struct reset_controller_dev *rc) +{ + return container_of(rc, struct reset_gpio_priv, rc); +} + +static int reset_gpio_assert(struct reset_controller_dev *rc, unsigned long id) +{ + struct reset_gpio_priv *priv = rc_to_reset_gpio(rc); + + gpiod_set_value_cansleep(priv->reset, 1); + + return 0; +} + +static int reset_gpio_deassert(struct reset_controller_dev *rc, + unsigned long id) +{ + struct reset_gpio_priv *priv = rc_to_reset_gpio(rc); + + gpiod_set_value_cansleep(priv->reset, 0); + + return 0; +} + +static int reset_gpio_status(struct reset_controller_dev *rc, unsigned long id) +{ + struct reset_gpio_priv *priv = rc_to_reset_gpio(rc); + + return gpiod_get_value_cansleep(priv->reset); +} + +static const struct reset_control_ops reset_gpio_ops = { + .assert = reset_gpio_assert, + .deassert = reset_gpio_deassert, + .status = reset_gpio_status, +}; + +static int reset_gpio_of_xlate(struct reset_controller_dev *rcdev, + const struct of_phandle_args *reset_spec) +{ + return reset_spec->args[0]; +} + +static void reset_gpio_of_node_put(void *data) +{ + of_node_put(data); +} + +static int reset_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct of_phandle_args *platdata = dev_get_platdata(dev); + struct reset_gpio_priv *priv; + int ret; + + if (!platdata) + return -EINVAL; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, &priv->rc); + + priv->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(priv->reset)) + return dev_err_probe(dev, PTR_ERR(priv->reset), + "Could not get reset gpios\n"); + + priv->rc.ops = &reset_gpio_ops; + priv->rc.owner = THIS_MODULE; + priv->rc.dev = dev; + priv->rc.of_args = platdata; + ret = devm_add_action_or_reset(dev, reset_gpio_of_node_put, + priv->rc.of_node); + if (ret) + return ret; + + /* Cells to match GPIO specifier, but it's not really used */ + priv->rc.of_reset_n_cells = 2; + priv->rc.of_xlate = reset_gpio_of_xlate; + priv->rc.nr_resets = 1; + + return devm_reset_controller_register(dev, &priv->rc); +} + +static const struct platform_device_id reset_gpio_ids[] = { + { .name = "reset-gpio", }, + {} +}; +MODULE_DEVICE_TABLE(platform, reset_gpio_ids); + +static struct platform_driver reset_gpio_driver = { + .probe = reset_gpio_probe, + .id_table = reset_gpio_ids, + .driver = { + .name = "reset-gpio", + }, +}; +module_platform_driver(reset_gpio_driver); + +MODULE_AUTHOR("Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>"); +MODULE_DESCRIPTION("Generic GPIO reset driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/reset/reset-simple.c b/drivers/reset/reset-simple.c index 818cabcc9fb7..276067839830 100644 --- a/drivers/reset/reset-simple.c +++ b/drivers/reset/reset-simple.c @@ -151,6 +151,8 @@ static const struct of_device_id reset_simple_dt_ids[] = { { .compatible = "snps,dw-high-reset" }, { .compatible = "snps,dw-low-reset", .data = &reset_simple_active_low }, + { .compatible = "sophgo,sg2042-reset", + .data = &reset_simple_active_low }, { /* sentinel */ }, }; diff --git a/drivers/soc/fsl/dpio/dpio-service.c b/drivers/soc/fsl/dpio/dpio-service.c index 1d2b27e3ea63..b811446e0fa5 100644 --- a/drivers/soc/fsl/dpio/dpio-service.c +++ b/drivers/soc/fsl/dpio/dpio-service.c @@ -523,7 +523,7 @@ int dpaa2_io_service_enqueue_multiple_desc_fq(struct dpaa2_io *d, struct qbman_eq_desc *ed; int i, ret; - ed = kcalloc(sizeof(struct qbman_eq_desc), 32, GFP_KERNEL); + ed = kcalloc(32, sizeof(struct qbman_eq_desc), GFP_KERNEL); if (!ed) return -ENOMEM; |