xref: /kernel/linux/linux-6.6/drivers/bus/bt1-apb.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Authors:
662306a36Sopenharmony_ci *   Serge Semin <Sergey.Semin@baikalelectronics.ru>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Baikal-T1 APB-bus driver
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/types.h>
1462306a36Sopenharmony_ci#include <linux/device.h>
1562306a36Sopenharmony_ci#include <linux/atomic.h>
1662306a36Sopenharmony_ci#include <linux/platform_device.h>
1762306a36Sopenharmony_ci#include <linux/interrupt.h>
1862306a36Sopenharmony_ci#include <linux/io.h>
1962306a36Sopenharmony_ci#include <linux/nmi.h>
2062306a36Sopenharmony_ci#include <linux/of.h>
2162306a36Sopenharmony_ci#include <linux/regmap.h>
2262306a36Sopenharmony_ci#include <linux/clk.h>
2362306a36Sopenharmony_ci#include <linux/reset.h>
2462306a36Sopenharmony_ci#include <linux/time64.h>
2562306a36Sopenharmony_ci#include <linux/clk.h>
2662306a36Sopenharmony_ci#include <linux/sysfs.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define APB_EHB_ISR			0x00
2962306a36Sopenharmony_ci#define APB_EHB_ISR_PENDING		BIT(0)
3062306a36Sopenharmony_ci#define APB_EHB_ISR_MASK		BIT(1)
3162306a36Sopenharmony_ci#define APB_EHB_ADDR			0x04
3262306a36Sopenharmony_ci#define APB_EHB_TIMEOUT			0x08
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define APB_EHB_TIMEOUT_MIN		0x000003FFU
3562306a36Sopenharmony_ci#define APB_EHB_TIMEOUT_MAX		0xFFFFFFFFU
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/*
3862306a36Sopenharmony_ci * struct bt1_apb - Baikal-T1 APB EHB private data
3962306a36Sopenharmony_ci * @dev: Pointer to the device structure.
4062306a36Sopenharmony_ci * @regs: APB EHB registers map.
4162306a36Sopenharmony_ci * @res: No-device error injection memory region.
4262306a36Sopenharmony_ci * @irq: Errors IRQ number.
4362306a36Sopenharmony_ci * @rate: APB-bus reference clock rate.
4462306a36Sopenharmony_ci * @pclk: APB-reference clock.
4562306a36Sopenharmony_ci * @prst: APB domain reset line.
4662306a36Sopenharmony_ci * @count: Number of errors detected.
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_cistruct bt1_apb {
4962306a36Sopenharmony_ci	struct device *dev;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	struct regmap *regs;
5262306a36Sopenharmony_ci	void __iomem *res;
5362306a36Sopenharmony_ci	int irq;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	unsigned long rate;
5662306a36Sopenharmony_ci	struct clk *pclk;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	struct reset_control *prst;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	atomic_t count;
6162306a36Sopenharmony_ci};
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic const struct regmap_config bt1_apb_regmap_cfg = {
6462306a36Sopenharmony_ci	.reg_bits = 32,
6562306a36Sopenharmony_ci	.val_bits = 32,
6662306a36Sopenharmony_ci	.reg_stride = 4,
6762306a36Sopenharmony_ci	.max_register = APB_EHB_TIMEOUT,
6862306a36Sopenharmony_ci	.fast_io = true
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic inline unsigned long bt1_apb_n_to_timeout_us(struct bt1_apb *apb, u32 n)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	u64 timeout = (u64)n * USEC_PER_SEC;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	do_div(timeout, apb->rate);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	return timeout;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic inline unsigned long bt1_apb_timeout_to_n_us(struct bt1_apb *apb,
8262306a36Sopenharmony_ci						    unsigned long timeout)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	u64 n = (u64)timeout * apb->rate;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	do_div(n, USEC_PER_SEC);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	return n;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic irqreturn_t bt1_apb_isr(int irq, void *data)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	struct bt1_apb *apb = data;
9562306a36Sopenharmony_ci	u32 addr = 0;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	regmap_read(apb->regs, APB_EHB_ADDR, &addr);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	dev_crit_ratelimited(apb->dev,
10062306a36Sopenharmony_ci		"APB-bus fault %d: Slave access timeout at 0x%08x\n",
10162306a36Sopenharmony_ci		atomic_inc_return(&apb->count),
10262306a36Sopenharmony_ci		addr);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	/*
10562306a36Sopenharmony_ci	 * Print backtrace on each CPU. This might be pointless if the fault
10662306a36Sopenharmony_ci	 * has happened on the same CPU as the IRQ handler is executed or
10762306a36Sopenharmony_ci	 * the other core proceeded further execution despite the error.
10862306a36Sopenharmony_ci	 * But if it's not, by looking at the trace we would get straight to
10962306a36Sopenharmony_ci	 * the cause of the problem.
11062306a36Sopenharmony_ci	 */
11162306a36Sopenharmony_ci	trigger_all_cpu_backtrace();
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	regmap_update_bits(apb->regs, APB_EHB_ISR, APB_EHB_ISR_PENDING, 0);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	return IRQ_HANDLED;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic void bt1_apb_clear_data(void *data)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct bt1_apb *apb = data;
12162306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(apb->dev);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	platform_set_drvdata(pdev, NULL);
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic struct bt1_apb *bt1_apb_create_data(struct platform_device *pdev)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
12962306a36Sopenharmony_ci	struct bt1_apb *apb;
13062306a36Sopenharmony_ci	int ret;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	apb = devm_kzalloc(dev, sizeof(*apb), GFP_KERNEL);
13362306a36Sopenharmony_ci	if (!apb)
13462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	ret = devm_add_action(dev, bt1_apb_clear_data, apb);
13762306a36Sopenharmony_ci	if (ret) {
13862306a36Sopenharmony_ci		dev_err(dev, "Can't add APB EHB data clear action\n");
13962306a36Sopenharmony_ci		return ERR_PTR(ret);
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	apb->dev = dev;
14362306a36Sopenharmony_ci	atomic_set(&apb->count, 0);
14462306a36Sopenharmony_ci	platform_set_drvdata(pdev, apb);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	return apb;
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistatic int bt1_apb_request_regs(struct bt1_apb *apb)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(apb->dev);
15262306a36Sopenharmony_ci	void __iomem *regs;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	regs = devm_platform_ioremap_resource_byname(pdev, "ehb");
15562306a36Sopenharmony_ci	if (IS_ERR(regs)) {
15662306a36Sopenharmony_ci		dev_err(apb->dev, "Couldn't map APB EHB registers\n");
15762306a36Sopenharmony_ci		return PTR_ERR(regs);
15862306a36Sopenharmony_ci	}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	apb->regs = devm_regmap_init_mmio(apb->dev, regs, &bt1_apb_regmap_cfg);
16162306a36Sopenharmony_ci	if (IS_ERR(apb->regs)) {
16262306a36Sopenharmony_ci		dev_err(apb->dev, "Couldn't create APB EHB regmap\n");
16362306a36Sopenharmony_ci		return PTR_ERR(apb->regs);
16462306a36Sopenharmony_ci	}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	apb->res = devm_platform_ioremap_resource_byname(pdev, "nodev");
16762306a36Sopenharmony_ci	if (IS_ERR(apb->res))
16862306a36Sopenharmony_ci		dev_err(apb->dev, "Couldn't map reserved region\n");
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	return PTR_ERR_OR_ZERO(apb->res);
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic int bt1_apb_request_rst(struct bt1_apb *apb)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	int ret;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	apb->prst = devm_reset_control_get_optional_exclusive(apb->dev, "prst");
17862306a36Sopenharmony_ci	if (IS_ERR(apb->prst))
17962306a36Sopenharmony_ci		return dev_err_probe(apb->dev, PTR_ERR(apb->prst),
18062306a36Sopenharmony_ci				     "Couldn't get reset control line\n");
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	ret = reset_control_deassert(apb->prst);
18362306a36Sopenharmony_ci	if (ret)
18462306a36Sopenharmony_ci		dev_err(apb->dev, "Failed to deassert the reset line\n");
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	return ret;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic void bt1_apb_disable_clk(void *data)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	struct bt1_apb *apb = data;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	clk_disable_unprepare(apb->pclk);
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic int bt1_apb_request_clk(struct bt1_apb *apb)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	int ret;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	apb->pclk = devm_clk_get(apb->dev, "pclk");
20162306a36Sopenharmony_ci	if (IS_ERR(apb->pclk))
20262306a36Sopenharmony_ci		return dev_err_probe(apb->dev, PTR_ERR(apb->pclk),
20362306a36Sopenharmony_ci				     "Couldn't get APB clock descriptor\n");
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	ret = clk_prepare_enable(apb->pclk);
20662306a36Sopenharmony_ci	if (ret) {
20762306a36Sopenharmony_ci		dev_err(apb->dev, "Couldn't enable the APB clock\n");
20862306a36Sopenharmony_ci		return ret;
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	ret = devm_add_action_or_reset(apb->dev, bt1_apb_disable_clk, apb);
21262306a36Sopenharmony_ci	if (ret) {
21362306a36Sopenharmony_ci		dev_err(apb->dev, "Can't add APB EHB clocks disable action\n");
21462306a36Sopenharmony_ci		return ret;
21562306a36Sopenharmony_ci	}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	apb->rate = clk_get_rate(apb->pclk);
21862306a36Sopenharmony_ci	if (!apb->rate) {
21962306a36Sopenharmony_ci		dev_err(apb->dev, "Invalid clock rate\n");
22062306a36Sopenharmony_ci		return -EINVAL;
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	return 0;
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic void bt1_apb_clear_irq(void *data)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	struct bt1_apb *apb = data;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	regmap_update_bits(apb->regs, APB_EHB_ISR, APB_EHB_ISR_MASK, 0);
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic int bt1_apb_request_irq(struct bt1_apb *apb)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(apb->dev);
23662306a36Sopenharmony_ci	int ret;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	apb->irq = platform_get_irq(pdev, 0);
23962306a36Sopenharmony_ci	if (apb->irq < 0)
24062306a36Sopenharmony_ci		return apb->irq;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	ret = devm_request_irq(apb->dev, apb->irq, bt1_apb_isr, IRQF_SHARED,
24362306a36Sopenharmony_ci			       "bt1-apb", apb);
24462306a36Sopenharmony_ci	if (ret) {
24562306a36Sopenharmony_ci		dev_err(apb->dev, "Couldn't request APB EHB IRQ\n");
24662306a36Sopenharmony_ci		return ret;
24762306a36Sopenharmony_ci	}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	ret = devm_add_action(apb->dev, bt1_apb_clear_irq, apb);
25062306a36Sopenharmony_ci	if (ret) {
25162306a36Sopenharmony_ci		dev_err(apb->dev, "Can't add APB EHB IRQs clear action\n");
25262306a36Sopenharmony_ci		return ret;
25362306a36Sopenharmony_ci	}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	/* Unmask IRQ and clear it' pending flag. */
25662306a36Sopenharmony_ci	regmap_update_bits(apb->regs, APB_EHB_ISR,
25762306a36Sopenharmony_ci			   APB_EHB_ISR_PENDING | APB_EHB_ISR_MASK,
25862306a36Sopenharmony_ci			   APB_EHB_ISR_MASK);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	return 0;
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic ssize_t count_show(struct device *dev, struct device_attribute *attr,
26462306a36Sopenharmony_ci			  char *buf)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	struct bt1_apb *apb = dev_get_drvdata(dev);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&apb->count));
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(count);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic ssize_t timeout_show(struct device *dev, struct device_attribute *attr,
27362306a36Sopenharmony_ci			    char *buf)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	struct bt1_apb *apb = dev_get_drvdata(dev);
27662306a36Sopenharmony_ci	unsigned long timeout;
27762306a36Sopenharmony_ci	int ret;
27862306a36Sopenharmony_ci	u32 n;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	ret = regmap_read(apb->regs, APB_EHB_TIMEOUT, &n);
28162306a36Sopenharmony_ci	if (ret)
28262306a36Sopenharmony_ci		return ret;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	timeout = bt1_apb_n_to_timeout_us(apb, n);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%lu\n", timeout);
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cistatic ssize_t timeout_store(struct device *dev,
29062306a36Sopenharmony_ci			     struct device_attribute *attr,
29162306a36Sopenharmony_ci			     const char *buf, size_t count)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	struct bt1_apb *apb = dev_get_drvdata(dev);
29462306a36Sopenharmony_ci	unsigned long timeout;
29562306a36Sopenharmony_ci	int ret;
29662306a36Sopenharmony_ci	u32 n;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	if (kstrtoul(buf, 0, &timeout) < 0)
29962306a36Sopenharmony_ci		return -EINVAL;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	n = bt1_apb_timeout_to_n_us(apb, timeout);
30262306a36Sopenharmony_ci	n = clamp(n, APB_EHB_TIMEOUT_MIN, APB_EHB_TIMEOUT_MAX);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	ret = regmap_write(apb->regs, APB_EHB_TIMEOUT, n);
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	return ret ?: count;
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_cistatic DEVICE_ATTR_RW(timeout);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistatic ssize_t inject_error_show(struct device *dev,
31162306a36Sopenharmony_ci				 struct device_attribute *attr, char *buf)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "Error injection: nodev irq\n");
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_cistatic ssize_t inject_error_store(struct device *dev,
31762306a36Sopenharmony_ci				  struct device_attribute *attr,
31862306a36Sopenharmony_ci				  const char *data, size_t count)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	struct bt1_apb *apb = dev_get_drvdata(dev);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	/*
32362306a36Sopenharmony_ci	 * Either dummy read from the unmapped address in the APB IO area
32462306a36Sopenharmony_ci	 * or manually set the IRQ status.
32562306a36Sopenharmony_ci	 */
32662306a36Sopenharmony_ci	if (sysfs_streq(data, "nodev"))
32762306a36Sopenharmony_ci		readl(apb->res);
32862306a36Sopenharmony_ci	else if (sysfs_streq(data, "irq"))
32962306a36Sopenharmony_ci		regmap_update_bits(apb->regs, APB_EHB_ISR, APB_EHB_ISR_PENDING,
33062306a36Sopenharmony_ci				   APB_EHB_ISR_PENDING);
33162306a36Sopenharmony_ci	else
33262306a36Sopenharmony_ci		return -EINVAL;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	return count;
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_cistatic DEVICE_ATTR_RW(inject_error);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_cistatic struct attribute *bt1_apb_sysfs_attrs[] = {
33962306a36Sopenharmony_ci	&dev_attr_count.attr,
34062306a36Sopenharmony_ci	&dev_attr_timeout.attr,
34162306a36Sopenharmony_ci	&dev_attr_inject_error.attr,
34262306a36Sopenharmony_ci	NULL
34362306a36Sopenharmony_ci};
34462306a36Sopenharmony_ciATTRIBUTE_GROUPS(bt1_apb_sysfs);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_cistatic void bt1_apb_remove_sysfs(void *data)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	struct bt1_apb *apb = data;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	device_remove_groups(apb->dev, bt1_apb_sysfs_groups);
35162306a36Sopenharmony_ci}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_cistatic int bt1_apb_init_sysfs(struct bt1_apb *apb)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	int ret;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	ret = device_add_groups(apb->dev, bt1_apb_sysfs_groups);
35862306a36Sopenharmony_ci	if (ret) {
35962306a36Sopenharmony_ci		dev_err(apb->dev, "Failed to create EHB APB sysfs nodes\n");
36062306a36Sopenharmony_ci		return ret;
36162306a36Sopenharmony_ci	}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	ret = devm_add_action_or_reset(apb->dev, bt1_apb_remove_sysfs, apb);
36462306a36Sopenharmony_ci	if (ret)
36562306a36Sopenharmony_ci		dev_err(apb->dev, "Can't add APB EHB sysfs remove action\n");
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	return ret;
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic int bt1_apb_probe(struct platform_device *pdev)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	struct bt1_apb *apb;
37362306a36Sopenharmony_ci	int ret;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	apb = bt1_apb_create_data(pdev);
37662306a36Sopenharmony_ci	if (IS_ERR(apb))
37762306a36Sopenharmony_ci		return PTR_ERR(apb);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	ret = bt1_apb_request_regs(apb);
38062306a36Sopenharmony_ci	if (ret)
38162306a36Sopenharmony_ci		return ret;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	ret = bt1_apb_request_rst(apb);
38462306a36Sopenharmony_ci	if (ret)
38562306a36Sopenharmony_ci		return ret;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	ret = bt1_apb_request_clk(apb);
38862306a36Sopenharmony_ci	if (ret)
38962306a36Sopenharmony_ci		return ret;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	ret = bt1_apb_request_irq(apb);
39262306a36Sopenharmony_ci	if (ret)
39362306a36Sopenharmony_ci		return ret;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	ret = bt1_apb_init_sysfs(apb);
39662306a36Sopenharmony_ci	if (ret)
39762306a36Sopenharmony_ci		return ret;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	return 0;
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_cistatic const struct of_device_id bt1_apb_of_match[] = {
40362306a36Sopenharmony_ci	{ .compatible = "baikal,bt1-apb" },
40462306a36Sopenharmony_ci	{ }
40562306a36Sopenharmony_ci};
40662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, bt1_apb_of_match);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_cistatic struct platform_driver bt1_apb_driver = {
40962306a36Sopenharmony_ci	.probe = bt1_apb_probe,
41062306a36Sopenharmony_ci	.driver = {
41162306a36Sopenharmony_ci		.name = "bt1-apb",
41262306a36Sopenharmony_ci		.of_match_table = bt1_apb_of_match
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci};
41562306a36Sopenharmony_cimodule_platform_driver(bt1_apb_driver);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ciMODULE_AUTHOR("Serge Semin <Sergey.Semin@baikalelectronics.ru>");
41862306a36Sopenharmony_ciMODULE_DESCRIPTION("Baikal-T1 APB-bus driver");
419