18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * CAN bus driver for the Freescale MPC5xxx embedded CPU. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2004-2005 Andrey Volkov <avolkov@varma-el.com>, 68c2ecf20Sopenharmony_ci * Varma Electronics Oy 78c2ecf20Sopenharmony_ci * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com> 88c2ecf20Sopenharmony_ci * Copyright (C) 2009 Wolfram Sang, Pengutronix <kernel@pengutronix.de> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 158c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 168c2ecf20Sopenharmony_ci#include <linux/can/dev.h> 178c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 188c2ecf20Sopenharmony_ci#include <sysdev/fsl_soc.h> 198c2ecf20Sopenharmony_ci#include <linux/clk.h> 208c2ecf20Sopenharmony_ci#include <linux/io.h> 218c2ecf20Sopenharmony_ci#include <asm/mpc52xx.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "mscan.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define DRV_NAME "mpc5xxx_can" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistruct mpc5xxx_can_data { 288c2ecf20Sopenharmony_ci unsigned int type; 298c2ecf20Sopenharmony_ci u32 (*get_clock)(struct platform_device *ofdev, const char *clock_name, 308c2ecf20Sopenharmony_ci int *mscan_clksrc); 318c2ecf20Sopenharmony_ci void (*put_clock)(struct platform_device *ofdev); 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_MPC52xx 358c2ecf20Sopenharmony_cistatic const struct of_device_id mpc52xx_cdm_ids[] = { 368c2ecf20Sopenharmony_ci { .compatible = "fsl,mpc5200-cdm", }, 378c2ecf20Sopenharmony_ci {} 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic u32 mpc52xx_can_get_clock(struct platform_device *ofdev, 418c2ecf20Sopenharmony_ci const char *clock_name, int *mscan_clksrc) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci unsigned int pvr; 448c2ecf20Sopenharmony_ci struct mpc52xx_cdm __iomem *cdm; 458c2ecf20Sopenharmony_ci struct device_node *np_cdm; 468c2ecf20Sopenharmony_ci unsigned int freq; 478c2ecf20Sopenharmony_ci u32 val; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci pvr = mfspr(SPRN_PVR); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci /* 528c2ecf20Sopenharmony_ci * Either the oscillator clock (SYS_XTAL_IN) or the IP bus clock 538c2ecf20Sopenharmony_ci * (IP_CLK) can be selected as MSCAN clock source. According to 548c2ecf20Sopenharmony_ci * the MPC5200 user's manual, the oscillator clock is the better 558c2ecf20Sopenharmony_ci * choice as it has less jitter. For this reason, it is selected 568c2ecf20Sopenharmony_ci * by default. Unfortunately, it can not be selected for the old 578c2ecf20Sopenharmony_ci * MPC5200 Rev. A chips due to a hardware bug (check errata). 588c2ecf20Sopenharmony_ci */ 598c2ecf20Sopenharmony_ci if (clock_name && strcmp(clock_name, "ip") == 0) 608c2ecf20Sopenharmony_ci *mscan_clksrc = MSCAN_CLKSRC_BUS; 618c2ecf20Sopenharmony_ci else 628c2ecf20Sopenharmony_ci *mscan_clksrc = MSCAN_CLKSRC_XTAL; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci freq = mpc5xxx_get_bus_frequency(ofdev->dev.of_node); 658c2ecf20Sopenharmony_ci if (!freq) 668c2ecf20Sopenharmony_ci return 0; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (*mscan_clksrc == MSCAN_CLKSRC_BUS || pvr == 0x80822011) 698c2ecf20Sopenharmony_ci return freq; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci /* Determine SYS_XTAL_IN frequency from the clock domain settings */ 728c2ecf20Sopenharmony_ci np_cdm = of_find_matching_node(NULL, mpc52xx_cdm_ids); 738c2ecf20Sopenharmony_ci if (!np_cdm) { 748c2ecf20Sopenharmony_ci dev_err(&ofdev->dev, "can't get clock node!\n"); 758c2ecf20Sopenharmony_ci return 0; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci cdm = of_iomap(np_cdm, 0); 788c2ecf20Sopenharmony_ci if (!cdm) { 798c2ecf20Sopenharmony_ci of_node_put(np_cdm); 808c2ecf20Sopenharmony_ci dev_err(&ofdev->dev, "can't map clock node!\n"); 818c2ecf20Sopenharmony_ci return 0; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (in_8(&cdm->ipb_clk_sel) & 0x1) 858c2ecf20Sopenharmony_ci freq *= 2; 868c2ecf20Sopenharmony_ci val = in_be32(&cdm->rstcfg); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci freq *= (val & (1 << 5)) ? 8 : 4; 898c2ecf20Sopenharmony_ci freq /= (val & (1 << 6)) ? 12 : 16; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci of_node_put(np_cdm); 928c2ecf20Sopenharmony_ci iounmap(cdm); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci return freq; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci#else /* !CONFIG_PPC_MPC52xx */ 978c2ecf20Sopenharmony_cistatic u32 mpc52xx_can_get_clock(struct platform_device *ofdev, 988c2ecf20Sopenharmony_ci const char *clock_name, int *mscan_clksrc) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci return 0; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci#endif /* CONFIG_PPC_MPC52xx */ 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_MPC512x 1058c2ecf20Sopenharmony_cistatic u32 mpc512x_can_get_clock(struct platform_device *ofdev, 1068c2ecf20Sopenharmony_ci const char *clock_source, int *mscan_clksrc) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci struct device_node *np; 1098c2ecf20Sopenharmony_ci u32 clockdiv; 1108c2ecf20Sopenharmony_ci enum { 1118c2ecf20Sopenharmony_ci CLK_FROM_AUTO, 1128c2ecf20Sopenharmony_ci CLK_FROM_IPS, 1138c2ecf20Sopenharmony_ci CLK_FROM_SYS, 1148c2ecf20Sopenharmony_ci CLK_FROM_REF, 1158c2ecf20Sopenharmony_ci } clk_from; 1168c2ecf20Sopenharmony_ci struct clk *clk_in, *clk_can; 1178c2ecf20Sopenharmony_ci unsigned long freq_calc; 1188c2ecf20Sopenharmony_ci struct mscan_priv *priv; 1198c2ecf20Sopenharmony_ci struct clk *clk_ipg; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci /* the caller passed in the clock source spec that was read from 1228c2ecf20Sopenharmony_ci * the device tree, get the optional clock divider as well 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_ci np = ofdev->dev.of_node; 1258c2ecf20Sopenharmony_ci clockdiv = 1; 1268c2ecf20Sopenharmony_ci of_property_read_u32(np, "fsl,mscan-clock-divider", &clockdiv); 1278c2ecf20Sopenharmony_ci dev_dbg(&ofdev->dev, "device tree specs: clk src[%s] div[%d]\n", 1288c2ecf20Sopenharmony_ci clock_source ? clock_source : "<NULL>", clockdiv); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci /* when clock-source is 'ip', the CANCTL1[CLKSRC] bit needs to 1318c2ecf20Sopenharmony_ci * get set, and the 'ips' clock is the input to the MSCAN 1328c2ecf20Sopenharmony_ci * component 1338c2ecf20Sopenharmony_ci * 1348c2ecf20Sopenharmony_ci * for clock-source values of 'ref' or 'sys' the CANCTL1[CLKSRC] 1358c2ecf20Sopenharmony_ci * bit needs to get cleared, an optional clock-divider may have 1368c2ecf20Sopenharmony_ci * been specified (the default value is 1), the appropriate 1378c2ecf20Sopenharmony_ci * MSCAN related MCLK is the input to the MSCAN component 1388c2ecf20Sopenharmony_ci * 1398c2ecf20Sopenharmony_ci * in the absence of a clock-source spec, first an optimal clock 1408c2ecf20Sopenharmony_ci * gets determined based on the 'sys' clock, if that fails the 1418c2ecf20Sopenharmony_ci * 'ref' clock is used 1428c2ecf20Sopenharmony_ci */ 1438c2ecf20Sopenharmony_ci clk_from = CLK_FROM_AUTO; 1448c2ecf20Sopenharmony_ci if (clock_source) { 1458c2ecf20Sopenharmony_ci /* interpret the device tree's spec for the clock source */ 1468c2ecf20Sopenharmony_ci if (!strcmp(clock_source, "ip")) 1478c2ecf20Sopenharmony_ci clk_from = CLK_FROM_IPS; 1488c2ecf20Sopenharmony_ci else if (!strcmp(clock_source, "sys")) 1498c2ecf20Sopenharmony_ci clk_from = CLK_FROM_SYS; 1508c2ecf20Sopenharmony_ci else if (!strcmp(clock_source, "ref")) 1518c2ecf20Sopenharmony_ci clk_from = CLK_FROM_REF; 1528c2ecf20Sopenharmony_ci else 1538c2ecf20Sopenharmony_ci goto err_invalid; 1548c2ecf20Sopenharmony_ci dev_dbg(&ofdev->dev, "got a clk source spec[%d]\n", clk_from); 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci if (clk_from == CLK_FROM_AUTO) { 1578c2ecf20Sopenharmony_ci /* no spec so far, try the 'sys' clock; round to the 1588c2ecf20Sopenharmony_ci * next MHz and see if we can get a multiple of 16MHz 1598c2ecf20Sopenharmony_ci */ 1608c2ecf20Sopenharmony_ci dev_dbg(&ofdev->dev, "no clk source spec, trying SYS\n"); 1618c2ecf20Sopenharmony_ci clk_in = devm_clk_get(&ofdev->dev, "sys"); 1628c2ecf20Sopenharmony_ci if (IS_ERR(clk_in)) 1638c2ecf20Sopenharmony_ci goto err_notavail; 1648c2ecf20Sopenharmony_ci freq_calc = clk_get_rate(clk_in); 1658c2ecf20Sopenharmony_ci freq_calc += 499999; 1668c2ecf20Sopenharmony_ci freq_calc /= 1000000; 1678c2ecf20Sopenharmony_ci freq_calc *= 1000000; 1688c2ecf20Sopenharmony_ci if ((freq_calc % 16000000) == 0) { 1698c2ecf20Sopenharmony_ci clk_from = CLK_FROM_SYS; 1708c2ecf20Sopenharmony_ci clockdiv = freq_calc / 16000000; 1718c2ecf20Sopenharmony_ci dev_dbg(&ofdev->dev, 1728c2ecf20Sopenharmony_ci "clk fit, sys[%lu] div[%d] freq[%lu]\n", 1738c2ecf20Sopenharmony_ci freq_calc, clockdiv, freq_calc / clockdiv); 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci if (clk_from == CLK_FROM_AUTO) { 1778c2ecf20Sopenharmony_ci /* no spec so far, use the 'ref' clock */ 1788c2ecf20Sopenharmony_ci dev_dbg(&ofdev->dev, "no clk source spec, trying REF\n"); 1798c2ecf20Sopenharmony_ci clk_in = devm_clk_get(&ofdev->dev, "ref"); 1808c2ecf20Sopenharmony_ci if (IS_ERR(clk_in)) 1818c2ecf20Sopenharmony_ci goto err_notavail; 1828c2ecf20Sopenharmony_ci clk_from = CLK_FROM_REF; 1838c2ecf20Sopenharmony_ci freq_calc = clk_get_rate(clk_in); 1848c2ecf20Sopenharmony_ci dev_dbg(&ofdev->dev, 1858c2ecf20Sopenharmony_ci "clk fit, ref[%lu] (no div) freq[%lu]\n", 1868c2ecf20Sopenharmony_ci freq_calc, freq_calc); 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* select IPS or MCLK as the MSCAN input (returned to the caller), 1908c2ecf20Sopenharmony_ci * setup the MCLK mux source and rate if applicable, apply the 1918c2ecf20Sopenharmony_ci * optionally specified or derived above divider, and determine 1928c2ecf20Sopenharmony_ci * the actual resulting clock rate to return to the caller 1938c2ecf20Sopenharmony_ci */ 1948c2ecf20Sopenharmony_ci switch (clk_from) { 1958c2ecf20Sopenharmony_ci case CLK_FROM_IPS: 1968c2ecf20Sopenharmony_ci clk_can = devm_clk_get(&ofdev->dev, "ips"); 1978c2ecf20Sopenharmony_ci if (IS_ERR(clk_can)) 1988c2ecf20Sopenharmony_ci goto err_notavail; 1998c2ecf20Sopenharmony_ci priv = netdev_priv(dev_get_drvdata(&ofdev->dev)); 2008c2ecf20Sopenharmony_ci priv->clk_can = clk_can; 2018c2ecf20Sopenharmony_ci freq_calc = clk_get_rate(clk_can); 2028c2ecf20Sopenharmony_ci *mscan_clksrc = MSCAN_CLKSRC_IPS; 2038c2ecf20Sopenharmony_ci dev_dbg(&ofdev->dev, "clk from IPS, clksrc[%d] freq[%lu]\n", 2048c2ecf20Sopenharmony_ci *mscan_clksrc, freq_calc); 2058c2ecf20Sopenharmony_ci break; 2068c2ecf20Sopenharmony_ci case CLK_FROM_SYS: 2078c2ecf20Sopenharmony_ci case CLK_FROM_REF: 2088c2ecf20Sopenharmony_ci clk_can = devm_clk_get(&ofdev->dev, "mclk"); 2098c2ecf20Sopenharmony_ci if (IS_ERR(clk_can)) 2108c2ecf20Sopenharmony_ci goto err_notavail; 2118c2ecf20Sopenharmony_ci priv = netdev_priv(dev_get_drvdata(&ofdev->dev)); 2128c2ecf20Sopenharmony_ci priv->clk_can = clk_can; 2138c2ecf20Sopenharmony_ci if (clk_from == CLK_FROM_SYS) 2148c2ecf20Sopenharmony_ci clk_in = devm_clk_get(&ofdev->dev, "sys"); 2158c2ecf20Sopenharmony_ci if (clk_from == CLK_FROM_REF) 2168c2ecf20Sopenharmony_ci clk_in = devm_clk_get(&ofdev->dev, "ref"); 2178c2ecf20Sopenharmony_ci if (IS_ERR(clk_in)) 2188c2ecf20Sopenharmony_ci goto err_notavail; 2198c2ecf20Sopenharmony_ci clk_set_parent(clk_can, clk_in); 2208c2ecf20Sopenharmony_ci freq_calc = clk_get_rate(clk_in); 2218c2ecf20Sopenharmony_ci freq_calc /= clockdiv; 2228c2ecf20Sopenharmony_ci clk_set_rate(clk_can, freq_calc); 2238c2ecf20Sopenharmony_ci freq_calc = clk_get_rate(clk_can); 2248c2ecf20Sopenharmony_ci *mscan_clksrc = MSCAN_CLKSRC_BUS; 2258c2ecf20Sopenharmony_ci dev_dbg(&ofdev->dev, "clk from MCLK, clksrc[%d] freq[%lu]\n", 2268c2ecf20Sopenharmony_ci *mscan_clksrc, freq_calc); 2278c2ecf20Sopenharmony_ci break; 2288c2ecf20Sopenharmony_ci default: 2298c2ecf20Sopenharmony_ci goto err_invalid; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /* the above clk_can item is used for the bitrate, access to 2338c2ecf20Sopenharmony_ci * the peripheral's register set needs the clk_ipg item 2348c2ecf20Sopenharmony_ci */ 2358c2ecf20Sopenharmony_ci clk_ipg = devm_clk_get(&ofdev->dev, "ipg"); 2368c2ecf20Sopenharmony_ci if (IS_ERR(clk_ipg)) 2378c2ecf20Sopenharmony_ci goto err_notavail_ipg; 2388c2ecf20Sopenharmony_ci if (clk_prepare_enable(clk_ipg)) 2398c2ecf20Sopenharmony_ci goto err_notavail_ipg; 2408c2ecf20Sopenharmony_ci priv = netdev_priv(dev_get_drvdata(&ofdev->dev)); 2418c2ecf20Sopenharmony_ci priv->clk_ipg = clk_ipg; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci /* return the determined clock source rate */ 2448c2ecf20Sopenharmony_ci return freq_calc; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cierr_invalid: 2478c2ecf20Sopenharmony_ci dev_err(&ofdev->dev, "invalid clock source specification\n"); 2488c2ecf20Sopenharmony_ci /* clock source rate could not get determined */ 2498c2ecf20Sopenharmony_ci return 0; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cierr_notavail: 2528c2ecf20Sopenharmony_ci dev_err(&ofdev->dev, "cannot acquire or setup bitrate clock source\n"); 2538c2ecf20Sopenharmony_ci /* clock source rate could not get determined */ 2548c2ecf20Sopenharmony_ci return 0; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cierr_notavail_ipg: 2578c2ecf20Sopenharmony_ci dev_err(&ofdev->dev, "cannot acquire or setup register clock\n"); 2588c2ecf20Sopenharmony_ci /* clock source rate could not get determined */ 2598c2ecf20Sopenharmony_ci return 0; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic void mpc512x_can_put_clock(struct platform_device *ofdev) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci struct mscan_priv *priv; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci priv = netdev_priv(dev_get_drvdata(&ofdev->dev)); 2678c2ecf20Sopenharmony_ci if (priv->clk_ipg) 2688c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clk_ipg); 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci#else /* !CONFIG_PPC_MPC512x */ 2718c2ecf20Sopenharmony_cistatic u32 mpc512x_can_get_clock(struct platform_device *ofdev, 2728c2ecf20Sopenharmony_ci const char *clock_name, int *mscan_clksrc) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci return 0; 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci#define mpc512x_can_put_clock NULL 2778c2ecf20Sopenharmony_ci#endif /* CONFIG_PPC_MPC512x */ 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic const struct of_device_id mpc5xxx_can_table[]; 2808c2ecf20Sopenharmony_cistatic int mpc5xxx_can_probe(struct platform_device *ofdev) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci const struct of_device_id *match; 2838c2ecf20Sopenharmony_ci const struct mpc5xxx_can_data *data; 2848c2ecf20Sopenharmony_ci struct device_node *np = ofdev->dev.of_node; 2858c2ecf20Sopenharmony_ci struct net_device *dev; 2868c2ecf20Sopenharmony_ci struct mscan_priv *priv; 2878c2ecf20Sopenharmony_ci void __iomem *base; 2888c2ecf20Sopenharmony_ci const char *clock_name = NULL; 2898c2ecf20Sopenharmony_ci int irq, mscan_clksrc = 0; 2908c2ecf20Sopenharmony_ci int err = -ENOMEM; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci match = of_match_device(mpc5xxx_can_table, &ofdev->dev); 2938c2ecf20Sopenharmony_ci if (!match) 2948c2ecf20Sopenharmony_ci return -EINVAL; 2958c2ecf20Sopenharmony_ci data = match->data; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci base = of_iomap(np, 0); 2988c2ecf20Sopenharmony_ci if (!base) { 2998c2ecf20Sopenharmony_ci dev_err(&ofdev->dev, "couldn't ioremap\n"); 3008c2ecf20Sopenharmony_ci return err; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci irq = irq_of_parse_and_map(np, 0); 3048c2ecf20Sopenharmony_ci if (!irq) { 3058c2ecf20Sopenharmony_ci dev_err(&ofdev->dev, "no irq found\n"); 3068c2ecf20Sopenharmony_ci err = -ENODEV; 3078c2ecf20Sopenharmony_ci goto exit_unmap_mem; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci dev = alloc_mscandev(); 3118c2ecf20Sopenharmony_ci if (!dev) 3128c2ecf20Sopenharmony_ci goto exit_dispose_irq; 3138c2ecf20Sopenharmony_ci platform_set_drvdata(ofdev, dev); 3148c2ecf20Sopenharmony_ci SET_NETDEV_DEV(dev, &ofdev->dev); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 3178c2ecf20Sopenharmony_ci priv->reg_base = base; 3188c2ecf20Sopenharmony_ci dev->irq = irq; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci clock_name = of_get_property(np, "fsl,mscan-clock-source", NULL); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci BUG_ON(!data); 3238c2ecf20Sopenharmony_ci priv->type = data->type; 3248c2ecf20Sopenharmony_ci priv->can.clock.freq = data->get_clock(ofdev, clock_name, 3258c2ecf20Sopenharmony_ci &mscan_clksrc); 3268c2ecf20Sopenharmony_ci if (!priv->can.clock.freq) { 3278c2ecf20Sopenharmony_ci dev_err(&ofdev->dev, "couldn't get MSCAN clock properties\n"); 3288c2ecf20Sopenharmony_ci goto exit_put_clock; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci err = register_mscandev(dev, mscan_clksrc); 3328c2ecf20Sopenharmony_ci if (err) { 3338c2ecf20Sopenharmony_ci dev_err(&ofdev->dev, "registering %s failed (err=%d)\n", 3348c2ecf20Sopenharmony_ci DRV_NAME, err); 3358c2ecf20Sopenharmony_ci goto exit_put_clock; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci dev_info(&ofdev->dev, "MSCAN at 0x%p, irq %d, clock %d Hz\n", 3398c2ecf20Sopenharmony_ci priv->reg_base, dev->irq, priv->can.clock.freq); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci return 0; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ciexit_put_clock: 3448c2ecf20Sopenharmony_ci if (data->put_clock) 3458c2ecf20Sopenharmony_ci data->put_clock(ofdev); 3468c2ecf20Sopenharmony_ci free_candev(dev); 3478c2ecf20Sopenharmony_ciexit_dispose_irq: 3488c2ecf20Sopenharmony_ci irq_dispose_mapping(irq); 3498c2ecf20Sopenharmony_ciexit_unmap_mem: 3508c2ecf20Sopenharmony_ci iounmap(base); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci return err; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic int mpc5xxx_can_remove(struct platform_device *ofdev) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci const struct of_device_id *match; 3588c2ecf20Sopenharmony_ci const struct mpc5xxx_can_data *data; 3598c2ecf20Sopenharmony_ci struct net_device *dev = platform_get_drvdata(ofdev); 3608c2ecf20Sopenharmony_ci struct mscan_priv *priv = netdev_priv(dev); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci match = of_match_device(mpc5xxx_can_table, &ofdev->dev); 3638c2ecf20Sopenharmony_ci data = match ? match->data : NULL; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci unregister_mscandev(dev); 3668c2ecf20Sopenharmony_ci if (data && data->put_clock) 3678c2ecf20Sopenharmony_ci data->put_clock(ofdev); 3688c2ecf20Sopenharmony_ci iounmap(priv->reg_base); 3698c2ecf20Sopenharmony_ci irq_dispose_mapping(dev->irq); 3708c2ecf20Sopenharmony_ci free_candev(dev); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci return 0; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 3768c2ecf20Sopenharmony_cistatic struct mscan_regs saved_regs; 3778c2ecf20Sopenharmony_cistatic int mpc5xxx_can_suspend(struct platform_device *ofdev, pm_message_t state) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci struct net_device *dev = platform_get_drvdata(ofdev); 3808c2ecf20Sopenharmony_ci struct mscan_priv *priv = netdev_priv(dev); 3818c2ecf20Sopenharmony_ci struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci _memcpy_fromio(&saved_regs, regs, sizeof(*regs)); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci return 0; 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic int mpc5xxx_can_resume(struct platform_device *ofdev) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci struct net_device *dev = platform_get_drvdata(ofdev); 3918c2ecf20Sopenharmony_ci struct mscan_priv *priv = netdev_priv(dev); 3928c2ecf20Sopenharmony_ci struct mscan_regs *regs = (struct mscan_regs *)priv->reg_base; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci regs->canctl0 |= MSCAN_INITRQ; 3958c2ecf20Sopenharmony_ci while (!(regs->canctl1 & MSCAN_INITAK)) 3968c2ecf20Sopenharmony_ci udelay(10); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci regs->canctl1 = saved_regs.canctl1; 3998c2ecf20Sopenharmony_ci regs->canbtr0 = saved_regs.canbtr0; 4008c2ecf20Sopenharmony_ci regs->canbtr1 = saved_regs.canbtr1; 4018c2ecf20Sopenharmony_ci regs->canidac = saved_regs.canidac; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci /* restore masks, buffers etc. */ 4048c2ecf20Sopenharmony_ci _memcpy_toio(®s->canidar1_0, (void *)&saved_regs.canidar1_0, 4058c2ecf20Sopenharmony_ci sizeof(*regs) - offsetof(struct mscan_regs, canidar1_0)); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci regs->canctl0 &= ~MSCAN_INITRQ; 4088c2ecf20Sopenharmony_ci regs->cantbsel = saved_regs.cantbsel; 4098c2ecf20Sopenharmony_ci regs->canrier = saved_regs.canrier; 4108c2ecf20Sopenharmony_ci regs->cantier = saved_regs.cantier; 4118c2ecf20Sopenharmony_ci regs->canctl0 = saved_regs.canctl0; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci return 0; 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci#endif 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic const struct mpc5xxx_can_data mpc5200_can_data = { 4188c2ecf20Sopenharmony_ci .type = MSCAN_TYPE_MPC5200, 4198c2ecf20Sopenharmony_ci .get_clock = mpc52xx_can_get_clock, 4208c2ecf20Sopenharmony_ci /* .put_clock not applicable */ 4218c2ecf20Sopenharmony_ci}; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic const struct mpc5xxx_can_data mpc5121_can_data = { 4248c2ecf20Sopenharmony_ci .type = MSCAN_TYPE_MPC5121, 4258c2ecf20Sopenharmony_ci .get_clock = mpc512x_can_get_clock, 4268c2ecf20Sopenharmony_ci .put_clock = mpc512x_can_put_clock, 4278c2ecf20Sopenharmony_ci}; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_cistatic const struct of_device_id mpc5xxx_can_table[] = { 4308c2ecf20Sopenharmony_ci { .compatible = "fsl,mpc5200-mscan", .data = &mpc5200_can_data, }, 4318c2ecf20Sopenharmony_ci /* Note that only MPC5121 Rev. 2 (and later) is supported */ 4328c2ecf20Sopenharmony_ci { .compatible = "fsl,mpc5121-mscan", .data = &mpc5121_can_data, }, 4338c2ecf20Sopenharmony_ci {}, 4348c2ecf20Sopenharmony_ci}; 4358c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mpc5xxx_can_table); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic struct platform_driver mpc5xxx_can_driver = { 4388c2ecf20Sopenharmony_ci .driver = { 4398c2ecf20Sopenharmony_ci .name = "mpc5xxx_can", 4408c2ecf20Sopenharmony_ci .of_match_table = mpc5xxx_can_table, 4418c2ecf20Sopenharmony_ci }, 4428c2ecf20Sopenharmony_ci .probe = mpc5xxx_can_probe, 4438c2ecf20Sopenharmony_ci .remove = mpc5xxx_can_remove, 4448c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 4458c2ecf20Sopenharmony_ci .suspend = mpc5xxx_can_suspend, 4468c2ecf20Sopenharmony_ci .resume = mpc5xxx_can_resume, 4478c2ecf20Sopenharmony_ci#endif 4488c2ecf20Sopenharmony_ci}; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cimodule_platform_driver(mpc5xxx_can_driver); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ciMODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); 4538c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Freescale MPC5xxx CAN driver"); 4548c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 455