1*8f93662dSSerge Semin // SPDX-License-Identifier: GPL-2.0-only 2*8f93662dSSerge Semin /* 3*8f93662dSSerge Semin * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC 4*8f93662dSSerge Semin * 5*8f93662dSSerge Semin * Authors: 6*8f93662dSSerge Semin * Serge Semin <Sergey.Semin@baikalelectronics.ru> 7*8f93662dSSerge Semin * 8*8f93662dSSerge Semin * Baikal-T1 APB-bus driver 9*8f93662dSSerge Semin */ 10*8f93662dSSerge Semin 11*8f93662dSSerge Semin #include <linux/kernel.h> 12*8f93662dSSerge Semin #include <linux/module.h> 13*8f93662dSSerge Semin #include <linux/types.h> 14*8f93662dSSerge Semin #include <linux/device.h> 15*8f93662dSSerge Semin #include <linux/atomic.h> 16*8f93662dSSerge Semin #include <linux/platform_device.h> 17*8f93662dSSerge Semin #include <linux/interrupt.h> 18*8f93662dSSerge Semin #include <linux/nmi.h> 19*8f93662dSSerge Semin #include <linux/of.h> 20*8f93662dSSerge Semin #include <linux/regmap.h> 21*8f93662dSSerge Semin #include <linux/clk.h> 22*8f93662dSSerge Semin #include <linux/reset.h> 23*8f93662dSSerge Semin #include <linux/time64.h> 24*8f93662dSSerge Semin #include <linux/clk.h> 25*8f93662dSSerge Semin #include <linux/sysfs.h> 26*8f93662dSSerge Semin 27*8f93662dSSerge Semin #define APB_EHB_ISR 0x00 28*8f93662dSSerge Semin #define APB_EHB_ISR_PENDING BIT(0) 29*8f93662dSSerge Semin #define APB_EHB_ISR_MASK BIT(1) 30*8f93662dSSerge Semin #define APB_EHB_ADDR 0x04 31*8f93662dSSerge Semin #define APB_EHB_TIMEOUT 0x08 32*8f93662dSSerge Semin 33*8f93662dSSerge Semin #define APB_EHB_TIMEOUT_MIN 0x000003FFU 34*8f93662dSSerge Semin #define APB_EHB_TIMEOUT_MAX 0xFFFFFFFFU 35*8f93662dSSerge Semin 36*8f93662dSSerge Semin /* 37*8f93662dSSerge Semin * struct bt1_apb - Baikal-T1 APB EHB private data 38*8f93662dSSerge Semin * @dev: Pointer to the device structure. 39*8f93662dSSerge Semin * @regs: APB EHB registers map. 40*8f93662dSSerge Semin * @res: No-device error injection memory region. 41*8f93662dSSerge Semin * @irq: Errors IRQ number. 42*8f93662dSSerge Semin * @rate: APB-bus reference clock rate. 43*8f93662dSSerge Semin * @pclk: APB-reference clock. 44*8f93662dSSerge Semin * @prst: APB domain reset line. 45*8f93662dSSerge Semin * @count: Number of errors detected. 46*8f93662dSSerge Semin */ 47*8f93662dSSerge Semin struct bt1_apb { 48*8f93662dSSerge Semin struct device *dev; 49*8f93662dSSerge Semin 50*8f93662dSSerge Semin struct regmap *regs; 51*8f93662dSSerge Semin void __iomem *res; 52*8f93662dSSerge Semin int irq; 53*8f93662dSSerge Semin 54*8f93662dSSerge Semin unsigned long rate; 55*8f93662dSSerge Semin struct clk *pclk; 56*8f93662dSSerge Semin 57*8f93662dSSerge Semin struct reset_control *prst; 58*8f93662dSSerge Semin 59*8f93662dSSerge Semin atomic_t count; 60*8f93662dSSerge Semin }; 61*8f93662dSSerge Semin 62*8f93662dSSerge Semin static const struct regmap_config bt1_apb_regmap_cfg = { 63*8f93662dSSerge Semin .reg_bits = 32, 64*8f93662dSSerge Semin .val_bits = 32, 65*8f93662dSSerge Semin .reg_stride = 4, 66*8f93662dSSerge Semin .max_register = APB_EHB_TIMEOUT, 67*8f93662dSSerge Semin .fast_io = true 68*8f93662dSSerge Semin }; 69*8f93662dSSerge Semin 70*8f93662dSSerge Semin static inline unsigned long bt1_apb_n_to_timeout_us(struct bt1_apb *apb, u32 n) 71*8f93662dSSerge Semin { 72*8f93662dSSerge Semin u64 timeout = (u64)n * USEC_PER_SEC; 73*8f93662dSSerge Semin 74*8f93662dSSerge Semin do_div(timeout, apb->rate); 75*8f93662dSSerge Semin 76*8f93662dSSerge Semin return timeout; 77*8f93662dSSerge Semin 78*8f93662dSSerge Semin } 79*8f93662dSSerge Semin 80*8f93662dSSerge Semin static inline unsigned long bt1_apb_timeout_to_n_us(struct bt1_apb *apb, 81*8f93662dSSerge Semin unsigned long timeout) 82*8f93662dSSerge Semin { 83*8f93662dSSerge Semin u64 n = (u64)timeout * apb->rate; 84*8f93662dSSerge Semin 85*8f93662dSSerge Semin do_div(n, USEC_PER_SEC); 86*8f93662dSSerge Semin 87*8f93662dSSerge Semin return n; 88*8f93662dSSerge Semin 89*8f93662dSSerge Semin } 90*8f93662dSSerge Semin 91*8f93662dSSerge Semin static irqreturn_t bt1_apb_isr(int irq, void *data) 92*8f93662dSSerge Semin { 93*8f93662dSSerge Semin struct bt1_apb *apb = data; 94*8f93662dSSerge Semin u32 addr = 0; 95*8f93662dSSerge Semin 96*8f93662dSSerge Semin regmap_read(apb->regs, APB_EHB_ADDR, &addr); 97*8f93662dSSerge Semin 98*8f93662dSSerge Semin dev_crit_ratelimited(apb->dev, 99*8f93662dSSerge Semin "APB-bus fault %d: Slave access timeout at 0x%08x\n", 100*8f93662dSSerge Semin atomic_inc_return(&apb->count), 101*8f93662dSSerge Semin addr); 102*8f93662dSSerge Semin 103*8f93662dSSerge Semin /* 104*8f93662dSSerge Semin * Print backtrace on each CPU. This might be pointless if the fault 105*8f93662dSSerge Semin * has happened on the same CPU as the IRQ handler is executed or 106*8f93662dSSerge Semin * the other core proceeded further execution despite the error. 107*8f93662dSSerge Semin * But if it's not, by looking at the trace we would get straight to 108*8f93662dSSerge Semin * the cause of the problem. 109*8f93662dSSerge Semin */ 110*8f93662dSSerge Semin trigger_all_cpu_backtrace(); 111*8f93662dSSerge Semin 112*8f93662dSSerge Semin regmap_update_bits(apb->regs, APB_EHB_ISR, APB_EHB_ISR_PENDING, 0); 113*8f93662dSSerge Semin 114*8f93662dSSerge Semin return IRQ_HANDLED; 115*8f93662dSSerge Semin } 116*8f93662dSSerge Semin 117*8f93662dSSerge Semin static void bt1_apb_clear_data(void *data) 118*8f93662dSSerge Semin { 119*8f93662dSSerge Semin struct bt1_apb *apb = data; 120*8f93662dSSerge Semin struct platform_device *pdev = to_platform_device(apb->dev); 121*8f93662dSSerge Semin 122*8f93662dSSerge Semin platform_set_drvdata(pdev, NULL); 123*8f93662dSSerge Semin } 124*8f93662dSSerge Semin 125*8f93662dSSerge Semin static struct bt1_apb *bt1_apb_create_data(struct platform_device *pdev) 126*8f93662dSSerge Semin { 127*8f93662dSSerge Semin struct device *dev = &pdev->dev; 128*8f93662dSSerge Semin struct bt1_apb *apb; 129*8f93662dSSerge Semin int ret; 130*8f93662dSSerge Semin 131*8f93662dSSerge Semin apb = devm_kzalloc(dev, sizeof(*apb), GFP_KERNEL); 132*8f93662dSSerge Semin if (!apb) 133*8f93662dSSerge Semin return ERR_PTR(-ENOMEM); 134*8f93662dSSerge Semin 135*8f93662dSSerge Semin ret = devm_add_action(dev, bt1_apb_clear_data, apb); 136*8f93662dSSerge Semin if (ret) { 137*8f93662dSSerge Semin dev_err(dev, "Can't add APB EHB data clear action\n"); 138*8f93662dSSerge Semin return ERR_PTR(ret); 139*8f93662dSSerge Semin } 140*8f93662dSSerge Semin 141*8f93662dSSerge Semin apb->dev = dev; 142*8f93662dSSerge Semin atomic_set(&apb->count, 0); 143*8f93662dSSerge Semin platform_set_drvdata(pdev, apb); 144*8f93662dSSerge Semin 145*8f93662dSSerge Semin return apb; 146*8f93662dSSerge Semin } 147*8f93662dSSerge Semin 148*8f93662dSSerge Semin static int bt1_apb_request_regs(struct bt1_apb *apb) 149*8f93662dSSerge Semin { 150*8f93662dSSerge Semin struct platform_device *pdev = to_platform_device(apb->dev); 151*8f93662dSSerge Semin void __iomem *regs; 152*8f93662dSSerge Semin 153*8f93662dSSerge Semin regs = devm_platform_ioremap_resource_byname(pdev, "ehb"); 154*8f93662dSSerge Semin if (IS_ERR(regs)) { 155*8f93662dSSerge Semin dev_err(apb->dev, "Couldn't map APB EHB registers\n"); 156*8f93662dSSerge Semin return PTR_ERR(regs); 157*8f93662dSSerge Semin } 158*8f93662dSSerge Semin 159*8f93662dSSerge Semin apb->regs = devm_regmap_init_mmio(apb->dev, regs, &bt1_apb_regmap_cfg); 160*8f93662dSSerge Semin if (IS_ERR(apb->regs)) { 161*8f93662dSSerge Semin dev_err(apb->dev, "Couldn't create APB EHB regmap\n"); 162*8f93662dSSerge Semin return PTR_ERR(apb->regs); 163*8f93662dSSerge Semin } 164*8f93662dSSerge Semin 165*8f93662dSSerge Semin apb->res = devm_platform_ioremap_resource_byname(pdev, "nodev"); 166*8f93662dSSerge Semin if (IS_ERR(apb->res)) { 167*8f93662dSSerge Semin dev_err(apb->dev, "Couldn't map reserved region\n"); 168*8f93662dSSerge Semin return PTR_ERR(apb->res); 169*8f93662dSSerge Semin } 170*8f93662dSSerge Semin 171*8f93662dSSerge Semin return 0; 172*8f93662dSSerge Semin } 173*8f93662dSSerge Semin 174*8f93662dSSerge Semin static int bt1_apb_request_rst(struct bt1_apb *apb) 175*8f93662dSSerge Semin { 176*8f93662dSSerge Semin int ret; 177*8f93662dSSerge Semin 178*8f93662dSSerge Semin apb->prst = devm_reset_control_get_optional_exclusive(apb->dev, "prst"); 179*8f93662dSSerge Semin if (IS_ERR(apb->prst)) { 180*8f93662dSSerge Semin dev_warn(apb->dev, "Couldn't get reset control line\n"); 181*8f93662dSSerge Semin return PTR_ERR(apb->prst); 182*8f93662dSSerge Semin } 183*8f93662dSSerge Semin 184*8f93662dSSerge Semin ret = reset_control_deassert(apb->prst); 185*8f93662dSSerge Semin if (ret) 186*8f93662dSSerge Semin dev_err(apb->dev, "Failed to deassert the reset line\n"); 187*8f93662dSSerge Semin 188*8f93662dSSerge Semin return ret; 189*8f93662dSSerge Semin } 190*8f93662dSSerge Semin 191*8f93662dSSerge Semin static void bt1_apb_disable_clk(void *data) 192*8f93662dSSerge Semin { 193*8f93662dSSerge Semin struct bt1_apb *apb = data; 194*8f93662dSSerge Semin 195*8f93662dSSerge Semin clk_disable_unprepare(apb->pclk); 196*8f93662dSSerge Semin } 197*8f93662dSSerge Semin 198*8f93662dSSerge Semin static int bt1_apb_request_clk(struct bt1_apb *apb) 199*8f93662dSSerge Semin { 200*8f93662dSSerge Semin int ret; 201*8f93662dSSerge Semin 202*8f93662dSSerge Semin apb->pclk = devm_clk_get(apb->dev, "pclk"); 203*8f93662dSSerge Semin if (IS_ERR(apb->pclk)) { 204*8f93662dSSerge Semin dev_err(apb->dev, "Couldn't get APB clock descriptor\n"); 205*8f93662dSSerge Semin return PTR_ERR(apb->pclk); 206*8f93662dSSerge Semin } 207*8f93662dSSerge Semin 208*8f93662dSSerge Semin ret = clk_prepare_enable(apb->pclk); 209*8f93662dSSerge Semin if (ret) { 210*8f93662dSSerge Semin dev_err(apb->dev, "Couldn't enable the APB clock\n"); 211*8f93662dSSerge Semin return ret; 212*8f93662dSSerge Semin } 213*8f93662dSSerge Semin 214*8f93662dSSerge Semin ret = devm_add_action_or_reset(apb->dev, bt1_apb_disable_clk, apb); 215*8f93662dSSerge Semin if (ret) { 216*8f93662dSSerge Semin dev_err(apb->dev, "Can't add APB EHB clocks disable action\n"); 217*8f93662dSSerge Semin return ret; 218*8f93662dSSerge Semin } 219*8f93662dSSerge Semin 220*8f93662dSSerge Semin apb->rate = clk_get_rate(apb->pclk); 221*8f93662dSSerge Semin if (!apb->rate) { 222*8f93662dSSerge Semin dev_err(apb->dev, "Invalid clock rate\n"); 223*8f93662dSSerge Semin return -EINVAL; 224*8f93662dSSerge Semin } 225*8f93662dSSerge Semin 226*8f93662dSSerge Semin return 0; 227*8f93662dSSerge Semin } 228*8f93662dSSerge Semin 229*8f93662dSSerge Semin static void bt1_apb_clear_irq(void *data) 230*8f93662dSSerge Semin { 231*8f93662dSSerge Semin struct bt1_apb *apb = data; 232*8f93662dSSerge Semin 233*8f93662dSSerge Semin regmap_update_bits(apb->regs, APB_EHB_ISR, APB_EHB_ISR_MASK, 0); 234*8f93662dSSerge Semin } 235*8f93662dSSerge Semin 236*8f93662dSSerge Semin static int bt1_apb_request_irq(struct bt1_apb *apb) 237*8f93662dSSerge Semin { 238*8f93662dSSerge Semin struct platform_device *pdev = to_platform_device(apb->dev); 239*8f93662dSSerge Semin int ret; 240*8f93662dSSerge Semin 241*8f93662dSSerge Semin apb->irq = platform_get_irq(pdev, 0); 242*8f93662dSSerge Semin if (apb->irq < 0) 243*8f93662dSSerge Semin return apb->irq; 244*8f93662dSSerge Semin 245*8f93662dSSerge Semin ret = devm_request_irq(apb->dev, apb->irq, bt1_apb_isr, IRQF_SHARED, 246*8f93662dSSerge Semin "bt1-apb", apb); 247*8f93662dSSerge Semin if (ret) { 248*8f93662dSSerge Semin dev_err(apb->dev, "Couldn't request APB EHB IRQ\n"); 249*8f93662dSSerge Semin return ret; 250*8f93662dSSerge Semin } 251*8f93662dSSerge Semin 252*8f93662dSSerge Semin ret = devm_add_action(apb->dev, bt1_apb_clear_irq, apb); 253*8f93662dSSerge Semin if (ret) { 254*8f93662dSSerge Semin dev_err(apb->dev, "Can't add APB EHB IRQs clear action\n"); 255*8f93662dSSerge Semin return ret; 256*8f93662dSSerge Semin } 257*8f93662dSSerge Semin 258*8f93662dSSerge Semin /* Unmask IRQ and clear it' pending flag. */ 259*8f93662dSSerge Semin regmap_update_bits(apb->regs, APB_EHB_ISR, 260*8f93662dSSerge Semin APB_EHB_ISR_PENDING | APB_EHB_ISR_MASK, 261*8f93662dSSerge Semin APB_EHB_ISR_MASK); 262*8f93662dSSerge Semin 263*8f93662dSSerge Semin return 0; 264*8f93662dSSerge Semin } 265*8f93662dSSerge Semin 266*8f93662dSSerge Semin static ssize_t count_show(struct device *dev, struct device_attribute *attr, 267*8f93662dSSerge Semin char *buf) 268*8f93662dSSerge Semin { 269*8f93662dSSerge Semin struct bt1_apb *apb = dev_get_drvdata(dev); 270*8f93662dSSerge Semin 271*8f93662dSSerge Semin return scnprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&apb->count)); 272*8f93662dSSerge Semin } 273*8f93662dSSerge Semin static DEVICE_ATTR_RO(count); 274*8f93662dSSerge Semin 275*8f93662dSSerge Semin static ssize_t timeout_show(struct device *dev, struct device_attribute *attr, 276*8f93662dSSerge Semin char *buf) 277*8f93662dSSerge Semin { 278*8f93662dSSerge Semin struct bt1_apb *apb = dev_get_drvdata(dev); 279*8f93662dSSerge Semin unsigned long timeout; 280*8f93662dSSerge Semin int ret; 281*8f93662dSSerge Semin u32 n; 282*8f93662dSSerge Semin 283*8f93662dSSerge Semin ret = regmap_read(apb->regs, APB_EHB_TIMEOUT, &n); 284*8f93662dSSerge Semin if (ret) 285*8f93662dSSerge Semin return ret; 286*8f93662dSSerge Semin 287*8f93662dSSerge Semin timeout = bt1_apb_n_to_timeout_us(apb, n); 288*8f93662dSSerge Semin 289*8f93662dSSerge Semin return scnprintf(buf, PAGE_SIZE, "%lu\n", timeout); 290*8f93662dSSerge Semin } 291*8f93662dSSerge Semin 292*8f93662dSSerge Semin static ssize_t timeout_store(struct device *dev, 293*8f93662dSSerge Semin struct device_attribute *attr, 294*8f93662dSSerge Semin const char *buf, size_t count) 295*8f93662dSSerge Semin { 296*8f93662dSSerge Semin struct bt1_apb *apb = dev_get_drvdata(dev); 297*8f93662dSSerge Semin unsigned long timeout; 298*8f93662dSSerge Semin int ret; 299*8f93662dSSerge Semin u32 n; 300*8f93662dSSerge Semin 301*8f93662dSSerge Semin if (kstrtoul(buf, 0, &timeout) < 0) 302*8f93662dSSerge Semin return -EINVAL; 303*8f93662dSSerge Semin 304*8f93662dSSerge Semin n = bt1_apb_timeout_to_n_us(apb, timeout); 305*8f93662dSSerge Semin n = clamp(n, APB_EHB_TIMEOUT_MIN, APB_EHB_TIMEOUT_MAX); 306*8f93662dSSerge Semin 307*8f93662dSSerge Semin ret = regmap_write(apb->regs, APB_EHB_TIMEOUT, n); 308*8f93662dSSerge Semin 309*8f93662dSSerge Semin return ret ?: count; 310*8f93662dSSerge Semin } 311*8f93662dSSerge Semin static DEVICE_ATTR_RW(timeout); 312*8f93662dSSerge Semin 313*8f93662dSSerge Semin static ssize_t inject_error_show(struct device *dev, struct device_attribute *attr, 314*8f93662dSSerge Semin char *buf) 315*8f93662dSSerge Semin { 316*8f93662dSSerge Semin return scnprintf(buf, PAGE_SIZE, "Error injection: nodev irq\n"); 317*8f93662dSSerge Semin } 318*8f93662dSSerge Semin 319*8f93662dSSerge Semin static ssize_t inject_error_store(struct device *dev, 320*8f93662dSSerge Semin struct device_attribute *attr, 321*8f93662dSSerge Semin const char *data, size_t count) 322*8f93662dSSerge Semin { 323*8f93662dSSerge Semin struct bt1_apb *apb = dev_get_drvdata(dev); 324*8f93662dSSerge Semin 325*8f93662dSSerge Semin /* 326*8f93662dSSerge Semin * Either dummy read from the unmapped address in the APB IO area 327*8f93662dSSerge Semin * or manually set the IRQ status. 328*8f93662dSSerge Semin */ 329*8f93662dSSerge Semin if (!strncmp(data, "nodev", 5)) 330*8f93662dSSerge Semin readl(apb->res); 331*8f93662dSSerge Semin else if (!strncmp(data, "irq", 3)) 332*8f93662dSSerge Semin regmap_update_bits(apb->regs, APB_EHB_ISR, APB_EHB_ISR_PENDING, 333*8f93662dSSerge Semin APB_EHB_ISR_PENDING); 334*8f93662dSSerge Semin else 335*8f93662dSSerge Semin return -EINVAL; 336*8f93662dSSerge Semin 337*8f93662dSSerge Semin return count; 338*8f93662dSSerge Semin } 339*8f93662dSSerge Semin static DEVICE_ATTR_RW(inject_error); 340*8f93662dSSerge Semin 341*8f93662dSSerge Semin static struct attribute *bt1_apb_sysfs_attrs[] = { 342*8f93662dSSerge Semin &dev_attr_count.attr, 343*8f93662dSSerge Semin &dev_attr_timeout.attr, 344*8f93662dSSerge Semin &dev_attr_inject_error.attr, 345*8f93662dSSerge Semin NULL 346*8f93662dSSerge Semin }; 347*8f93662dSSerge Semin ATTRIBUTE_GROUPS(bt1_apb_sysfs); 348*8f93662dSSerge Semin 349*8f93662dSSerge Semin static void bt1_apb_remove_sysfs(void *data) 350*8f93662dSSerge Semin { 351*8f93662dSSerge Semin struct bt1_apb *apb = data; 352*8f93662dSSerge Semin 353*8f93662dSSerge Semin device_remove_groups(apb->dev, bt1_apb_sysfs_groups); 354*8f93662dSSerge Semin } 355*8f93662dSSerge Semin 356*8f93662dSSerge Semin static int bt1_apb_init_sysfs(struct bt1_apb *apb) 357*8f93662dSSerge Semin { 358*8f93662dSSerge Semin int ret; 359*8f93662dSSerge Semin 360*8f93662dSSerge Semin ret = device_add_groups(apb->dev, bt1_apb_sysfs_groups); 361*8f93662dSSerge Semin if (ret) { 362*8f93662dSSerge Semin dev_err(apb->dev, "Failed to create EHB APB sysfs nodes\n"); 363*8f93662dSSerge Semin return ret; 364*8f93662dSSerge Semin } 365*8f93662dSSerge Semin 366*8f93662dSSerge Semin ret = devm_add_action_or_reset(apb->dev, bt1_apb_remove_sysfs, apb); 367*8f93662dSSerge Semin if (ret) 368*8f93662dSSerge Semin dev_err(apb->dev, "Can't add APB EHB sysfs remove action\n"); 369*8f93662dSSerge Semin 370*8f93662dSSerge Semin return ret; 371*8f93662dSSerge Semin } 372*8f93662dSSerge Semin 373*8f93662dSSerge Semin static int bt1_apb_probe(struct platform_device *pdev) 374*8f93662dSSerge Semin { 375*8f93662dSSerge Semin struct bt1_apb *apb; 376*8f93662dSSerge Semin int ret; 377*8f93662dSSerge Semin 378*8f93662dSSerge Semin apb = bt1_apb_create_data(pdev); 379*8f93662dSSerge Semin if (IS_ERR(apb)) 380*8f93662dSSerge Semin return PTR_ERR(apb); 381*8f93662dSSerge Semin 382*8f93662dSSerge Semin ret = bt1_apb_request_regs(apb); 383*8f93662dSSerge Semin if (ret) 384*8f93662dSSerge Semin return ret; 385*8f93662dSSerge Semin 386*8f93662dSSerge Semin ret = bt1_apb_request_rst(apb); 387*8f93662dSSerge Semin if (ret) 388*8f93662dSSerge Semin return ret; 389*8f93662dSSerge Semin 390*8f93662dSSerge Semin ret = bt1_apb_request_clk(apb); 391*8f93662dSSerge Semin if (ret) 392*8f93662dSSerge Semin return ret; 393*8f93662dSSerge Semin 394*8f93662dSSerge Semin ret = bt1_apb_request_irq(apb); 395*8f93662dSSerge Semin if (ret) 396*8f93662dSSerge Semin return ret; 397*8f93662dSSerge Semin 398*8f93662dSSerge Semin ret = bt1_apb_init_sysfs(apb); 399*8f93662dSSerge Semin if (ret) 400*8f93662dSSerge Semin return ret; 401*8f93662dSSerge Semin 402*8f93662dSSerge Semin return 0; 403*8f93662dSSerge Semin } 404*8f93662dSSerge Semin 405*8f93662dSSerge Semin static const struct of_device_id bt1_apb_of_match[] = { 406*8f93662dSSerge Semin { .compatible = "baikal,bt1-apb" }, 407*8f93662dSSerge Semin { } 408*8f93662dSSerge Semin }; 409*8f93662dSSerge Semin MODULE_DEVICE_TABLE(of, bt1_apb_of_match); 410*8f93662dSSerge Semin 411*8f93662dSSerge Semin static struct platform_driver bt1_apb_driver = { 412*8f93662dSSerge Semin .probe = bt1_apb_probe, 413*8f93662dSSerge Semin .driver = { 414*8f93662dSSerge Semin .name = "bt1-apb", 415*8f93662dSSerge Semin .of_match_table = bt1_apb_of_match 416*8f93662dSSerge Semin } 417*8f93662dSSerge Semin }; 418*8f93662dSSerge Semin module_platform_driver(bt1_apb_driver); 419*8f93662dSSerge Semin 420*8f93662dSSerge Semin MODULE_AUTHOR("Serge Semin <Sergey.Semin@baikalelectronics.ru>"); 421*8f93662dSSerge Semin MODULE_DESCRIPTION("Baikal-T1 APB-bus driver"); 422*8f93662dSSerge Semin MODULE_LICENSE("GPL v2"); 423