can: m_can: Enable M_CAN version dependent initialization

This patch adapts the initialization of the M_CAN. So it can be used with
all versions >= 3.0.x.

Changes:
* Added version element to m_can_priv structure to hold M_CAN version.
* Renamed bittiming structs for version 3.0.x
* Added new bittiming structs for version >= 3.1.x
* Function alloc_m_can_dev takes 2 new arguments. The TX FIFO size and the
  base address of the module.
* Chip configuration for CAN_CTRLMODE_LOOPBACK is changed: Enabled
  CCCR_MON bit. In combination with TEST_LBCK it activates the internal
  loopback mode. Leaving CCCR_MON '0' results in external loopback mode.
* Clocks are temporarily enabled by platform_propbe function in order to
  allow read access to the Core Release register and the Control Register.
  Registers are used to detect M_CAN version and optional Non-ISO Feature.

Initialization of M_CAN for version >= 3.1.x:
* TX FIFO of M_CAN is used to transmit frames. The driver does not need to
  stop the tx queue after each frame sent.
* Initialization of TX Event FIFO is added.
* NON-ISO is fixed for all M_CAN versions < 3.2.x. Version 3.2.x _can_ have
  the NISO (Non-ISO) bit which can switch the mode of the M_CAN to Non-ISO
  mode. This bit does not have to be writeable. Therefore it is checked.
  If it is writable Non-ISO support is added to the controllers supported
  CAN modes.

New Functions:
* Function to check the Core Release version. The read value determines the
  behaviour of the driver.
* Function to check if the NISO bit for version >= 3.2.x is implemented.

Signed-off-by: Mario Huettel <mario.huettel@gmx.net>
This commit is contained in:
Mario Hüttel 2017-04-08 14:10:13 +02:00 committed by Mario Hüttel
parent 4d0ab5bc18
commit 16cd0aaa5a
1 changed files with 269 additions and 71 deletions

View File

@ -23,7 +23,7 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/iopoll.h>
#include <linux/can/dev.h>
/* napi related */
@ -107,6 +107,14 @@ enum m_can_mram_cfg {
MRAM_CFG_NUM,
};
/* Core Release Register (CREL) */
#define CREL_REL_SHIFT 28
#define CREL_REL_MASK (0xF << CREL_REL_SHIFT)
#define CREL_STEP_SHIFT 24
#define CREL_STEP_MASK (0xF << CREL_STEP_SHIFT)
#define CREL_SUBSTEP_SHIFT 20
#define CREL_SUBSTEP_MASK (0xF << CREL_SUBSTEP_SHIFT)
/* Data Bit Timing & Prescaler Register (DBTP) */
#define DBTP_TDC BIT(23)
#define DBTP_DBRP_SHIFT 16
@ -342,6 +350,7 @@ struct m_can_priv {
struct clk *cclk;
void __iomem *base;
u32 irqstatus;
int version;
/* message ram configuration */
void __iomem *mram_base;
@ -833,7 +842,7 @@ static irqreturn_t m_can_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
static const struct can_bittiming_const m_can_bittiming_const = {
static const struct can_bittiming_const m_can_bittiming_const_30X = {
.name = KBUILD_MODNAME,
.tseg1_min = 2, /* Time segment 1 = prop_seg + phase_seg1 */
.tseg1_max = 64,
@ -845,7 +854,7 @@ static const struct can_bittiming_const m_can_bittiming_const = {
.brp_inc = 1,
};
static const struct can_bittiming_const m_can_data_bittiming_const = {
static const struct can_bittiming_const m_can_data_bittiming_const_30X = {
.name = KBUILD_MODNAME,
.tseg1_min = 2, /* Time segment 1 = prop_seg + phase_seg1 */
.tseg1_max = 16,
@ -857,6 +866,30 @@ static const struct can_bittiming_const m_can_data_bittiming_const = {
.brp_inc = 1,
};
static const struct can_bittiming_const m_can_bittiming_const_31X = {
.name = KBUILD_MODNAME,
.tseg1_min = 2, /* Time segment 1 = prop_seg + phase_seg1 */
.tseg1_max = 256,
.tseg2_min = 1, /* Time segment 2 = phase_seg2 */
.tseg2_max = 128,
.sjw_max = 128,
.brp_min = 1,
.brp_max = 512,
.brp_inc = 1,
};
static const struct can_bittiming_const m_can_data_bittiming_const_31X = {
.name = KBUILD_MODNAME,
.tseg1_min = 1, /* Time segment 1 = prop_seg + phase_seg1 */
.tseg1_max = 32,
.tseg2_min = 1, /* Time segment 2 = phase_seg2 */
.tseg2_max = 16,
.sjw_max = 16,
.brp_min = 1,
.brp_max = 32,
.brp_inc = 1,
};
static int m_can_set_bittiming(struct net_device *dev)
{
struct m_can_priv *priv = netdev_priv(dev);
@ -928,29 +961,53 @@ static void m_can_chip_config(struct net_device *dev)
priv->mcfg[MRAM_RXF1].off);
cccr = m_can_read(priv, M_CAN_CCCR);
cccr &= ~(CCCR_TEST | CCCR_MON | (CCCR_CMR_MASK << CCCR_CMR_SHIFT) |
(CCCR_CME_MASK << CCCR_CME_SHIFT));
test = m_can_read(priv, M_CAN_TEST);
test &= ~TEST_LBCK;
if (priv->version == 30) {
/* Version 3.0.x */
if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
cccr |= CCCR_MON;
cccr &= ~(CCCR_TEST | CCCR_MON |
(CCCR_CMR_MASK << CCCR_CMR_SHIFT) |
(CCCR_CME_MASK << CCCR_CME_SHIFT));
if (priv->can.ctrlmode & CAN_CTRLMODE_FD)
cccr |= CCCR_CME_CANFD_BRS << CCCR_CME_SHIFT;
} else {
/* Version 3.1.x or 3.2.x */
cccr &= ~(CCCR_TEST | CCCR_MON | CCCR_BRSE | CCCR_FDOE);
/* Only 3.2.x has NISO Bit implemented */
if (priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO)
cccr |= CCCR_NISO;
if (priv->can.ctrlmode & CAN_CTRLMODE_FD)
cccr |= (CCCR_BRSE | CCCR_FDOE);
}
/* Loopback Mode */
if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) {
cccr |= CCCR_TEST;
cccr |= CCCR_TEST | CCCR_MON;
test |= TEST_LBCK;
}
if (priv->can.ctrlmode & CAN_CTRLMODE_FD)
cccr |= CCCR_CME_CANFD_BRS << CCCR_CME_SHIFT;
/* Enable Monitoring (all versions) */
if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
cccr |= CCCR_MON;
/* Write config */
m_can_write(priv, M_CAN_CCCR, cccr);
m_can_write(priv, M_CAN_TEST, test);
/* enable interrupts */
/* Enable interrupts */
m_can_write(priv, M_CAN_IR, IR_ALL_INT);
if (!(priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING))
m_can_write(priv, M_CAN_IE, IR_ALL_INT & ~IR_ERR_LEC_30X);
if (priv->version == 30)
m_can_write(priv, M_CAN_IE, IR_ALL_INT &
~(IR_ERR_LEC_30X));
else
m_can_write(priv, M_CAN_IE, IR_ALL_INT &
~(IR_ERR_LEC_31X));
else
m_can_write(priv, M_CAN_IE, IR_ALL_INT);
@ -994,33 +1051,140 @@ static void free_m_can_dev(struct net_device *dev)
free_candev(dev);
}
static struct net_device *alloc_m_can_dev(void)
/* Checks core release number of M_CAN
* returns 0 if an unsupported device is detected
* else it returns the release and step coded as:
* return value = 10 * <release> + 1 * <step>
*/
static int m_can_check_core_release(void * __iomem m_can_base)
{
u32 crel_reg;
u8 rel;
u8 step;
int res;
struct m_can_priv temp_priv = {
.base = m_can_base
};
/* Read Core Release Version and split into version number
* Example: Version 3.2.1 => rel = 3; step = 2; substep = 1;
*/
crel_reg = m_can_read(&temp_priv, M_CAN_CREL);
rel = (u8)((crel_reg & CREL_REL_MASK) >> CREL_REL_SHIFT);
step = (u8)((crel_reg & CREL_STEP_MASK) >> CREL_STEP_SHIFT);
if (rel == 3) {
/* M_CAN v3.x.y: create return value */
res = 30 + step;
} else {
/* Unsupported M_CAN version */
res = 0;
}
return res;
}
/* Selectable Non ISO support only in version 3.2.x
* This function checks if the bit is writable.
*/
static bool m_can_niso_supported(const struct m_can_priv *priv)
{
u32 cccr_reg, cccr_poll;
int niso_timeout;
m_can_config_endisable(priv, true);
cccr_reg = m_can_read(priv, M_CAN_CCCR);
cccr_reg |= CCCR_NISO;
m_can_write(priv, M_CAN_CCCR, cccr_reg);
niso_timeout = readl_poll_timeout((priv->base + M_CAN_CCCR), cccr_poll,
(cccr_poll == cccr_reg), 0, 10);
/* Clear NISO */
cccr_reg &= ~(CCCR_NISO);
m_can_write(priv, M_CAN_CCCR, cccr_reg);
m_can_config_endisable(priv, false);
/* return false if time out (-ETIMEDOUT), else return true */
return !niso_timeout;
}
static struct net_device *alloc_m_can_dev(struct platform_device *pdev,
void __iomem *addr, u32 tx_fifo_size)
{
struct net_device *dev;
struct m_can_priv *priv;
int m_can_version;
unsigned int echo_buffer_count;
dev = alloc_candev(sizeof(*priv), 1);
if (!dev)
return NULL;
m_can_version = m_can_check_core_release(addr);
/* return if unsupported version */
if (!m_can_version) {
dev = NULL;
goto return_dev;
}
/* If version < 3.1.x, then only one echo buffer is used */
echo_buffer_count = ((m_can_version == 30)
? 1U
: (unsigned int)tx_fifo_size);
dev = alloc_candev(sizeof(*priv), echo_buffer_count);
if (!dev) {
dev = NULL;
goto return_dev;
}
priv = netdev_priv(dev);
netif_napi_add(dev, &priv->napi, m_can_poll, M_CAN_NAPI_WEIGHT);
/* Shared properties of all M_CAN versions */
priv->version = m_can_version;
priv->dev = dev;
priv->can.bittiming_const = &m_can_bittiming_const;
priv->can.data_bittiming_const = &m_can_data_bittiming_const;
priv->base = addr;
priv->can.do_set_mode = m_can_set_mode;
priv->can.do_get_berr_counter = m_can_get_berr_counter;
/* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.0.1 */
can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD_NON_ISO);
/* CAN_CTRLMODE_FD_NON_ISO can not be changed with M_CAN IP v3.0.1 */
/* Set M_CAN supported operations */
priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
CAN_CTRLMODE_LISTENONLY |
CAN_CTRLMODE_BERR_REPORTING |
CAN_CTRLMODE_FD;
/* Set properties depending on M_CAN version */
switch (priv->version) {
case 30:
/* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.0.x */
can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD_NON_ISO);
priv->can.bittiming_const = &m_can_bittiming_const_30X;
priv->can.data_bittiming_const =
&m_can_data_bittiming_const_30X;
break;
case 31:
/* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.1.x */
can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD_NON_ISO);
priv->can.bittiming_const = &m_can_bittiming_const_31X;
priv->can.data_bittiming_const =
&m_can_data_bittiming_const_31X;
break;
case 32:
priv->can.bittiming_const = &m_can_bittiming_const_31X;
priv->can.data_bittiming_const =
&m_can_data_bittiming_const_31X;
priv->can.ctrlmode_supported |= (m_can_niso_supported(priv)
? CAN_CTRLMODE_FD_NON_ISO
: 0);
break;
default:
/* Unsupported device: free candev */
free_m_can_dev(dev);
dev_err(&pdev->dev, "Unsupported version number: %2d",
priv->version);
dev = NULL;
break;
}
return_dev:
return dev;
}
@ -1167,58 +1331,37 @@ static int register_m_can_dev(struct net_device *dev)
return register_candev(dev);
}
static int m_can_of_parse_mram(struct platform_device *pdev,
struct m_can_priv *priv)
static void m_can_of_parse_mram(struct m_can_priv *priv,
const u32 *mram_config_vals)
{
struct device_node *np = pdev->dev.of_node;
struct resource *res;
void __iomem *addr;
u32 out_val[MRAM_CFG_LEN];
int i, start, end, ret;
int i, start, end;
/* message ram could be shared */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram");
if (!res)
return -ENODEV;
addr = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!addr)
return -ENOMEM;
/* get message ram configuration */
ret = of_property_read_u32_array(np, "bosch,mram-cfg",
out_val, sizeof(out_val) / 4);
if (ret) {
dev_err(&pdev->dev, "can not get message ram configuration\n");
return -ENODEV;
}
priv->mram_base = addr;
priv->mcfg[MRAM_SIDF].off = out_val[0];
priv->mcfg[MRAM_SIDF].num = out_val[1];
priv->mcfg[MRAM_SIDF].off = mram_config_vals[0];
priv->mcfg[MRAM_SIDF].num = mram_config_vals[1];
priv->mcfg[MRAM_XIDF].off = priv->mcfg[MRAM_SIDF].off +
priv->mcfg[MRAM_SIDF].num * SIDF_ELEMENT_SIZE;
priv->mcfg[MRAM_XIDF].num = out_val[2];
priv->mcfg[MRAM_XIDF].num = mram_config_vals[2];
priv->mcfg[MRAM_RXF0].off = priv->mcfg[MRAM_XIDF].off +
priv->mcfg[MRAM_XIDF].num * XIDF_ELEMENT_SIZE;
priv->mcfg[MRAM_RXF0].num = out_val[3] &
priv->mcfg[MRAM_RXF0].num = mram_config_vals[3] &
(RXFC_FS_MASK >> RXFC_FS_SHIFT);
priv->mcfg[MRAM_RXF1].off = priv->mcfg[MRAM_RXF0].off +
priv->mcfg[MRAM_RXF0].num * RXF0_ELEMENT_SIZE;
priv->mcfg[MRAM_RXF1].num = out_val[4] &
priv->mcfg[MRAM_RXF1].num = mram_config_vals[4] &
(RXFC_FS_MASK >> RXFC_FS_SHIFT);
priv->mcfg[MRAM_RXB].off = priv->mcfg[MRAM_RXF1].off +
priv->mcfg[MRAM_RXF1].num * RXF1_ELEMENT_SIZE;
priv->mcfg[MRAM_RXB].num = out_val[5];
priv->mcfg[MRAM_RXB].num = mram_config_vals[5];
priv->mcfg[MRAM_TXE].off = priv->mcfg[MRAM_RXB].off +
priv->mcfg[MRAM_RXB].num * RXB_ELEMENT_SIZE;
priv->mcfg[MRAM_TXE].num = out_val[6];
priv->mcfg[MRAM_TXE].num = mram_config_vals[6];
priv->mcfg[MRAM_TXB].off = priv->mcfg[MRAM_TXE].off +
priv->mcfg[MRAM_TXE].num * TXE_ELEMENT_SIZE;
priv->mcfg[MRAM_TXB].num = out_val[7] &
priv->mcfg[MRAM_TXB].num = mram_config_vals[7] &
(TXBC_NDTB_MASK >> TXBC_NDTB_SHIFT);
dev_dbg(&pdev->dev, "mram_base %p sidf 0x%x %d xidf 0x%x %d rxf0 0x%x %d rxf1 0x%x %d rxb 0x%x %d txe 0x%x %d txb 0x%x %d\n",
dev_dbg(priv->device,
"mram_base %p sidf 0x%x %d xidf 0x%x %d rxf0 0x%x %d rxf1 0x%x %d rxb 0x%x %d txe 0x%x %d txb 0x%x %d\n",
priv->mram_base,
priv->mcfg[MRAM_SIDF].off, priv->mcfg[MRAM_SIDF].num,
priv->mcfg[MRAM_XIDF].off, priv->mcfg[MRAM_XIDF].num,
@ -1237,7 +1380,6 @@ static int m_can_of_parse_mram(struct platform_device *pdev,
for (i = start; i < end; i += 4)
writel(0x0, priv->mram_base + i);
return 0;
}
static int m_can_plat_probe(struct platform_device *pdev)
@ -1246,38 +1388,86 @@ static int m_can_plat_probe(struct platform_device *pdev)
struct m_can_priv *priv;
struct resource *res;
void __iomem *addr;
void __iomem *mram_addr;
struct clk *hclk, *cclk;
int irq, ret;
struct device_node *np;
u32 mram_config_vals[MRAM_CFG_LEN];
u32 tx_fifo_size;
np = pdev->dev.of_node;
hclk = devm_clk_get(&pdev->dev, "hclk");
cclk = devm_clk_get(&pdev->dev, "cclk");
if (IS_ERR(hclk) || IS_ERR(cclk)) {
dev_err(&pdev->dev, "no clock found\n");
return -ENODEV;
ret = -ENODEV;
goto failed_ret;
}
/* Enable clocks. Necessary to read Core Release in order to determine
* M_CAN version
*/
ret = clk_prepare_enable(hclk);
if (ret)
goto disable_hclk_ret;
ret = clk_prepare_enable(cclk);
if (ret)
goto disable_cclk_ret;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "m_can");
addr = devm_ioremap_resource(&pdev->dev, res);
irq = platform_get_irq_byname(pdev, "int0");
if (IS_ERR(addr) || irq < 0)
return -EINVAL;
if (IS_ERR(addr) || irq < 0) {
ret = -EINVAL;
goto disable_cclk_ret;
}
/* message ram could be shared */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "message_ram");
if (!res) {
ret = -ENODEV;
goto disable_cclk_ret;
}
mram_addr = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!mram_addr) {
ret = -ENOMEM;
goto disable_cclk_ret;
}
/* get message ram configuration */
ret = of_property_read_u32_array(np, "bosch,mram-cfg",
mram_config_vals,
sizeof(mram_config_vals) / 4);
if (ret) {
dev_err(&pdev->dev, "Could not get Message RAM configuration.");
goto disable_cclk_ret;
}
/* Get TX FIFO size
* Defines the total amount of echo buffers for loopback
*/
tx_fifo_size = mram_config_vals[7];
/* allocate the m_can device */
dev = alloc_m_can_dev();
if (!dev)
return -ENOMEM;
dev = alloc_m_can_dev(pdev, addr, tx_fifo_size);
if (!dev) {
ret = -ENOMEM;
goto disable_cclk_ret;
}
priv = netdev_priv(dev);
dev->irq = irq;
priv->base = addr;
priv->device = &pdev->dev;
priv->hclk = hclk;
priv->cclk = cclk;
priv->can.clock.freq = clk_get_rate(cclk);
priv->mram_base = mram_addr;
ret = m_can_of_parse_mram(pdev, priv);
if (ret)
goto failed_free_dev;
m_can_of_parse_mram(priv, mram_config_vals);
platform_set_drvdata(pdev, dev);
SET_NETDEV_DEV(dev, &pdev->dev);
@ -1291,13 +1481,21 @@ static int m_can_plat_probe(struct platform_device *pdev)
devm_can_led_init(dev);
dev_info(&pdev->dev, "%s device registered (irq=%d)\n",
KBUILD_MODNAME, dev->irq);
dev_info(&pdev->dev, "%s device registered (irq=%d, version=%d)\n",
KBUILD_MODNAME, dev->irq, priv->version);
return 0;
/* Probe finished
* Stop clocks. They will be reactivated once the M_CAN device is opened
*/
goto disable_cclk_ret;
failed_free_dev:
free_m_can_dev(dev);
disable_cclk_ret:
clk_disable_unprepare(cclk);
disable_hclk_ret:
clk_disable_unprepare(hclk);
failed_ret:
return ret;
}