18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2005 Sascha Hauer, Pengutronix 48c2ecf20Sopenharmony_ci * Copyright (C) 2007 Wolfgang Grandegger <wg@grandegger.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/kernel.h> 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 108c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/pci.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/irq.h> 158c2ecf20Sopenharmony_ci#include <linux/can/dev.h> 168c2ecf20Sopenharmony_ci#include <linux/can/platform/sja1000.h> 178c2ecf20Sopenharmony_ci#include <linux/io.h> 188c2ecf20Sopenharmony_ci#include <linux/of.h> 198c2ecf20Sopenharmony_ci#include <linux/of_device.h> 208c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "sja1000.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define DRV_NAME "sja1000_platform" 258c2ecf20Sopenharmony_ci#define SP_CAN_CLOCK (16000000 / 2) 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); 288c2ecf20Sopenharmony_ciMODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); 298c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Socket-CAN driver for SJA1000 on the platform bus"); 308c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:" DRV_NAME); 318c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistruct sja1000_of_data { 348c2ecf20Sopenharmony_ci size_t priv_sz; 358c2ecf20Sopenharmony_ci int (*init)(struct sja1000_priv *priv, struct device_node *of); 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistruct technologic_priv { 398c2ecf20Sopenharmony_ci spinlock_t io_lock; 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic u8 sp_read_reg8(const struct sja1000_priv *priv, int reg) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci return ioread8(priv->reg_base + reg); 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic void sp_write_reg8(const struct sja1000_priv *priv, int reg, u8 val) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci iowrite8(val, priv->reg_base + reg); 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic u8 sp_read_reg16(const struct sja1000_priv *priv, int reg) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci return ioread8(priv->reg_base + reg * 2); 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic void sp_write_reg16(const struct sja1000_priv *priv, int reg, u8 val) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci iowrite8(val, priv->reg_base + reg * 2); 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic u8 sp_read_reg32(const struct sja1000_priv *priv, int reg) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci return ioread8(priv->reg_base + reg * 4); 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic void sp_write_reg32(const struct sja1000_priv *priv, int reg, u8 val) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci iowrite8(val, priv->reg_base + reg * 4); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic u8 sp_technologic_read_reg16(const struct sja1000_priv *priv, int reg) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci struct technologic_priv *tp = priv->priv; 758c2ecf20Sopenharmony_ci unsigned long flags; 768c2ecf20Sopenharmony_ci u8 val; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci spin_lock_irqsave(&tp->io_lock, flags); 798c2ecf20Sopenharmony_ci iowrite16(reg, priv->reg_base + 0); 808c2ecf20Sopenharmony_ci val = ioread16(priv->reg_base + 2); 818c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&tp->io_lock, flags); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci return val; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic void sp_technologic_write_reg16(const struct sja1000_priv *priv, 878c2ecf20Sopenharmony_ci int reg, u8 val) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci struct technologic_priv *tp = priv->priv; 908c2ecf20Sopenharmony_ci unsigned long flags; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci spin_lock_irqsave(&tp->io_lock, flags); 938c2ecf20Sopenharmony_ci iowrite16(reg, priv->reg_base + 0); 948c2ecf20Sopenharmony_ci iowrite16(val, priv->reg_base + 2); 958c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&tp->io_lock, flags); 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic int sp_technologic_init(struct sja1000_priv *priv, struct device_node *of) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci struct technologic_priv *tp = priv->priv; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci priv->read_reg = sp_technologic_read_reg16; 1038c2ecf20Sopenharmony_ci priv->write_reg = sp_technologic_write_reg16; 1048c2ecf20Sopenharmony_ci spin_lock_init(&tp->io_lock); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return 0; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic void sp_populate(struct sja1000_priv *priv, 1108c2ecf20Sopenharmony_ci struct sja1000_platform_data *pdata, 1118c2ecf20Sopenharmony_ci unsigned long resource_mem_flags) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci /* The CAN clock frequency is half the oscillator clock frequency */ 1148c2ecf20Sopenharmony_ci priv->can.clock.freq = pdata->osc_freq / 2; 1158c2ecf20Sopenharmony_ci priv->ocr = pdata->ocr; 1168c2ecf20Sopenharmony_ci priv->cdr = pdata->cdr; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci switch (resource_mem_flags & IORESOURCE_MEM_TYPE_MASK) { 1198c2ecf20Sopenharmony_ci case IORESOURCE_MEM_32BIT: 1208c2ecf20Sopenharmony_ci priv->read_reg = sp_read_reg32; 1218c2ecf20Sopenharmony_ci priv->write_reg = sp_write_reg32; 1228c2ecf20Sopenharmony_ci break; 1238c2ecf20Sopenharmony_ci case IORESOURCE_MEM_16BIT: 1248c2ecf20Sopenharmony_ci priv->read_reg = sp_read_reg16; 1258c2ecf20Sopenharmony_ci priv->write_reg = sp_write_reg16; 1268c2ecf20Sopenharmony_ci break; 1278c2ecf20Sopenharmony_ci case IORESOURCE_MEM_8BIT: 1288c2ecf20Sopenharmony_ci default: 1298c2ecf20Sopenharmony_ci priv->read_reg = sp_read_reg8; 1308c2ecf20Sopenharmony_ci priv->write_reg = sp_write_reg8; 1318c2ecf20Sopenharmony_ci break; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic void sp_populate_of(struct sja1000_priv *priv, struct device_node *of) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci int err; 1388c2ecf20Sopenharmony_ci u32 prop; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci err = of_property_read_u32(of, "reg-io-width", &prop); 1418c2ecf20Sopenharmony_ci if (err) 1428c2ecf20Sopenharmony_ci prop = 1; /* 8 bit is default */ 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci switch (prop) { 1458c2ecf20Sopenharmony_ci case 4: 1468c2ecf20Sopenharmony_ci priv->read_reg = sp_read_reg32; 1478c2ecf20Sopenharmony_ci priv->write_reg = sp_write_reg32; 1488c2ecf20Sopenharmony_ci break; 1498c2ecf20Sopenharmony_ci case 2: 1508c2ecf20Sopenharmony_ci priv->read_reg = sp_read_reg16; 1518c2ecf20Sopenharmony_ci priv->write_reg = sp_write_reg16; 1528c2ecf20Sopenharmony_ci break; 1538c2ecf20Sopenharmony_ci case 1: 1548c2ecf20Sopenharmony_ci default: 1558c2ecf20Sopenharmony_ci priv->read_reg = sp_read_reg8; 1568c2ecf20Sopenharmony_ci priv->write_reg = sp_write_reg8; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci err = of_property_read_u32(of, "nxp,external-clock-frequency", &prop); 1608c2ecf20Sopenharmony_ci if (!err) 1618c2ecf20Sopenharmony_ci priv->can.clock.freq = prop / 2; 1628c2ecf20Sopenharmony_ci else 1638c2ecf20Sopenharmony_ci priv->can.clock.freq = SP_CAN_CLOCK; /* default */ 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci err = of_property_read_u32(of, "nxp,tx-output-mode", &prop); 1668c2ecf20Sopenharmony_ci if (!err) 1678c2ecf20Sopenharmony_ci priv->ocr |= prop & OCR_MODE_MASK; 1688c2ecf20Sopenharmony_ci else 1698c2ecf20Sopenharmony_ci priv->ocr |= OCR_MODE_NORMAL; /* default */ 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci err = of_property_read_u32(of, "nxp,tx-output-config", &prop); 1728c2ecf20Sopenharmony_ci if (!err) 1738c2ecf20Sopenharmony_ci priv->ocr |= (prop << OCR_TX_SHIFT) & OCR_TX_MASK; 1748c2ecf20Sopenharmony_ci else 1758c2ecf20Sopenharmony_ci priv->ocr |= OCR_TX0_PULLDOWN; /* default */ 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci err = of_property_read_u32(of, "nxp,clock-out-frequency", &prop); 1788c2ecf20Sopenharmony_ci if (!err && prop) { 1798c2ecf20Sopenharmony_ci u32 divider = priv->can.clock.freq * 2 / prop; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci if (divider > 1) 1828c2ecf20Sopenharmony_ci priv->cdr |= divider / 2 - 1; 1838c2ecf20Sopenharmony_ci else 1848c2ecf20Sopenharmony_ci priv->cdr |= CDR_CLKOUT_MASK; 1858c2ecf20Sopenharmony_ci } else { 1868c2ecf20Sopenharmony_ci priv->cdr |= CDR_CLK_OFF; /* default */ 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (!of_property_read_bool(of, "nxp,no-comparator-bypass")) 1908c2ecf20Sopenharmony_ci priv->cdr |= CDR_CBP; /* default */ 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic struct sja1000_of_data technologic_data = { 1948c2ecf20Sopenharmony_ci .priv_sz = sizeof(struct technologic_priv), 1958c2ecf20Sopenharmony_ci .init = sp_technologic_init, 1968c2ecf20Sopenharmony_ci}; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic const struct of_device_id sp_of_table[] = { 1998c2ecf20Sopenharmony_ci { .compatible = "nxp,sja1000", .data = NULL, }, 2008c2ecf20Sopenharmony_ci { .compatible = "technologic,sja1000", .data = &technologic_data, }, 2018c2ecf20Sopenharmony_ci { /* sentinel */ }, 2028c2ecf20Sopenharmony_ci}; 2038c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, sp_of_table); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic int sp_probe(struct platform_device *pdev) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci int err, irq = 0; 2088c2ecf20Sopenharmony_ci void __iomem *addr; 2098c2ecf20Sopenharmony_ci struct net_device *dev; 2108c2ecf20Sopenharmony_ci struct sja1000_priv *priv; 2118c2ecf20Sopenharmony_ci struct resource *res_mem, *res_irq = NULL; 2128c2ecf20Sopenharmony_ci struct sja1000_platform_data *pdata; 2138c2ecf20Sopenharmony_ci struct device_node *of = pdev->dev.of_node; 2148c2ecf20Sopenharmony_ci const struct of_device_id *of_id; 2158c2ecf20Sopenharmony_ci const struct sja1000_of_data *of_data = NULL; 2168c2ecf20Sopenharmony_ci size_t priv_sz = 0; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci pdata = dev_get_platdata(&pdev->dev); 2198c2ecf20Sopenharmony_ci if (!pdata && !of) { 2208c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No platform data provided!\n"); 2218c2ecf20Sopenharmony_ci return -ENODEV; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 2258c2ecf20Sopenharmony_ci if (!res_mem) 2268c2ecf20Sopenharmony_ci return -ENODEV; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (!devm_request_mem_region(&pdev->dev, res_mem->start, 2298c2ecf20Sopenharmony_ci resource_size(res_mem), DRV_NAME)) 2308c2ecf20Sopenharmony_ci return -EBUSY; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci addr = devm_ioremap(&pdev->dev, res_mem->start, 2338c2ecf20Sopenharmony_ci resource_size(res_mem)); 2348c2ecf20Sopenharmony_ci if (!addr) 2358c2ecf20Sopenharmony_ci return -ENOMEM; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if (of) 2388c2ecf20Sopenharmony_ci irq = irq_of_parse_and_map(of, 0); 2398c2ecf20Sopenharmony_ci else 2408c2ecf20Sopenharmony_ci res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (!irq && !res_irq) 2438c2ecf20Sopenharmony_ci return -ENODEV; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci of_id = of_match_device(sp_of_table, &pdev->dev); 2468c2ecf20Sopenharmony_ci if (of_id && of_id->data) { 2478c2ecf20Sopenharmony_ci of_data = of_id->data; 2488c2ecf20Sopenharmony_ci priv_sz = of_data->priv_sz; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci dev = alloc_sja1000dev(priv_sz); 2528c2ecf20Sopenharmony_ci if (!dev) 2538c2ecf20Sopenharmony_ci return -ENOMEM; 2548c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (res_irq) { 2578c2ecf20Sopenharmony_ci irq = res_irq->start; 2588c2ecf20Sopenharmony_ci priv->irq_flags = res_irq->flags & IRQF_TRIGGER_MASK; 2598c2ecf20Sopenharmony_ci if (res_irq->flags & IORESOURCE_IRQ_SHAREABLE) 2608c2ecf20Sopenharmony_ci priv->irq_flags |= IRQF_SHARED; 2618c2ecf20Sopenharmony_ci } else { 2628c2ecf20Sopenharmony_ci priv->irq_flags = IRQF_SHARED; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci dev->irq = irq; 2668c2ecf20Sopenharmony_ci priv->reg_base = addr; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (of) { 2698c2ecf20Sopenharmony_ci sp_populate_of(priv, of); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (of_data && of_data->init) { 2728c2ecf20Sopenharmony_ci err = of_data->init(priv, of); 2738c2ecf20Sopenharmony_ci if (err) 2748c2ecf20Sopenharmony_ci goto exit_free; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci } else { 2778c2ecf20Sopenharmony_ci sp_populate(priv, pdata, res_mem->flags); 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, dev); 2818c2ecf20Sopenharmony_ci SET_NETDEV_DEV(dev, &pdev->dev); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci err = register_sja1000dev(dev); 2848c2ecf20Sopenharmony_ci if (err) { 2858c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "registering %s failed (err=%d)\n", 2868c2ecf20Sopenharmony_ci DRV_NAME, err); 2878c2ecf20Sopenharmony_ci goto exit_free; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "%s device registered (reg_base=%p, irq=%d)\n", 2918c2ecf20Sopenharmony_ci DRV_NAME, priv->reg_base, dev->irq); 2928c2ecf20Sopenharmony_ci return 0; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci exit_free: 2958c2ecf20Sopenharmony_ci free_sja1000dev(dev); 2968c2ecf20Sopenharmony_ci return err; 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cistatic int sp_remove(struct platform_device *pdev) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci struct net_device *dev = platform_get_drvdata(pdev); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci unregister_sja1000dev(dev); 3048c2ecf20Sopenharmony_ci free_sja1000dev(dev); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci return 0; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic struct platform_driver sp_driver = { 3108c2ecf20Sopenharmony_ci .probe = sp_probe, 3118c2ecf20Sopenharmony_ci .remove = sp_remove, 3128c2ecf20Sopenharmony_ci .driver = { 3138c2ecf20Sopenharmony_ci .name = DRV_NAME, 3148c2ecf20Sopenharmony_ci .of_match_table = sp_of_table, 3158c2ecf20Sopenharmony_ci }, 3168c2ecf20Sopenharmony_ci}; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cimodule_platform_driver(sp_driver); 319