162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* drivers/net/ethernet/micrel/ks8851.c 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright 2009 Simtec Electronics 562306a36Sopenharmony_ci * http://www.simtec.co.uk/ 662306a36Sopenharmony_ci * Ben Dooks <ben@simtec.co.uk> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/netdevice.h> 1562306a36Sopenharmony_ci#include <linux/etherdevice.h> 1662306a36Sopenharmony_ci#include <linux/ethtool.h> 1762306a36Sopenharmony_ci#include <linux/iopoll.h> 1862306a36Sopenharmony_ci#include <linux/mii.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/platform_device.h> 2162306a36Sopenharmony_ci#include <linux/of_net.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "ks8851.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic int msg_enable; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define BE3 0x8000 /* Byte Enable 3 */ 2862306a36Sopenharmony_ci#define BE2 0x4000 /* Byte Enable 2 */ 2962306a36Sopenharmony_ci#define BE1 0x2000 /* Byte Enable 1 */ 3062306a36Sopenharmony_ci#define BE0 0x1000 /* Byte Enable 0 */ 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/** 3362306a36Sopenharmony_ci * struct ks8851_net_par - KS8851 Parallel driver private data 3462306a36Sopenharmony_ci * @ks8851: KS8851 driver common private data 3562306a36Sopenharmony_ci * @lock: Lock to ensure that the device is not accessed when busy. 3662306a36Sopenharmony_ci * @hw_addr : start address of data register. 3762306a36Sopenharmony_ci * @hw_addr_cmd : start address of command register. 3862306a36Sopenharmony_ci * @cmd_reg_cache : command register cached. 3962306a36Sopenharmony_ci * 4062306a36Sopenharmony_ci * The @lock ensures that the chip is protected when certain operations are 4162306a36Sopenharmony_ci * in progress. When the read or write packet transfer is in progress, most 4262306a36Sopenharmony_ci * of the chip registers are not accessible until the transfer is finished 4362306a36Sopenharmony_ci * and the DMA has been de-asserted. 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_cistruct ks8851_net_par { 4662306a36Sopenharmony_ci struct ks8851_net ks8851; 4762306a36Sopenharmony_ci spinlock_t lock; 4862306a36Sopenharmony_ci void __iomem *hw_addr; 4962306a36Sopenharmony_ci void __iomem *hw_addr_cmd; 5062306a36Sopenharmony_ci u16 cmd_reg_cache; 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define to_ks8851_par(ks) container_of((ks), struct ks8851_net_par, ks8851) 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/** 5662306a36Sopenharmony_ci * ks8851_lock_par - register access lock 5762306a36Sopenharmony_ci * @ks: The chip state 5862306a36Sopenharmony_ci * @flags: Spinlock flags 5962306a36Sopenharmony_ci * 6062306a36Sopenharmony_ci * Claim chip register access lock 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_cistatic void ks8851_lock_par(struct ks8851_net *ks, unsigned long *flags) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci struct ks8851_net_par *ksp = to_ks8851_par(ks); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci spin_lock_irqsave(&ksp->lock, *flags); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/** 7062306a36Sopenharmony_ci * ks8851_unlock_par - register access unlock 7162306a36Sopenharmony_ci * @ks: The chip state 7262306a36Sopenharmony_ci * @flags: Spinlock flags 7362306a36Sopenharmony_ci * 7462306a36Sopenharmony_ci * Release chip register access lock 7562306a36Sopenharmony_ci */ 7662306a36Sopenharmony_cistatic void ks8851_unlock_par(struct ks8851_net *ks, unsigned long *flags) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci struct ks8851_net_par *ksp = to_ks8851_par(ks); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci spin_unlock_irqrestore(&ksp->lock, *flags); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/** 8462306a36Sopenharmony_ci * ks_check_endian - Check whether endianness of the bus is correct 8562306a36Sopenharmony_ci * @ks : The chip information 8662306a36Sopenharmony_ci * 8762306a36Sopenharmony_ci * The KS8851-16MLL EESK pin allows selecting the endianness of the 16bit 8862306a36Sopenharmony_ci * bus. To maintain optimum performance, the bus endianness should be set 8962306a36Sopenharmony_ci * such that it matches the endianness of the CPU. 9062306a36Sopenharmony_ci */ 9162306a36Sopenharmony_cistatic int ks_check_endian(struct ks8851_net *ks) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci struct ks8851_net_par *ksp = to_ks8851_par(ks); 9462306a36Sopenharmony_ci u16 cider; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci /* 9762306a36Sopenharmony_ci * Read CIDER register first, however read it the "wrong" way around. 9862306a36Sopenharmony_ci * If the endian strap on the KS8851-16MLL in incorrect and the chip 9962306a36Sopenharmony_ci * is operating in different endianness than the CPU, then the meaning 10062306a36Sopenharmony_ci * of BE[3:0] byte-enable bits is also swapped such that: 10162306a36Sopenharmony_ci * BE[3,2,1,0] becomes BE[1,0,3,2] 10262306a36Sopenharmony_ci * 10362306a36Sopenharmony_ci * Luckily for us, the byte-enable bits are the top four MSbits of 10462306a36Sopenharmony_ci * the address register and the CIDER register is at offset 0xc0. 10562306a36Sopenharmony_ci * Hence, by reading address 0xc0c0, which is not impacted by endian 10662306a36Sopenharmony_ci * swapping, we assert either BE[3:2] or BE[1:0] while reading the 10762306a36Sopenharmony_ci * CIDER register. 10862306a36Sopenharmony_ci * 10962306a36Sopenharmony_ci * If the bus configuration is correct, reading 0xc0c0 asserts 11062306a36Sopenharmony_ci * BE[3:2] and this read returns 0x0000, because to read register 11162306a36Sopenharmony_ci * with bottom two LSbits of address set to 0, BE[1:0] must be 11262306a36Sopenharmony_ci * asserted. 11362306a36Sopenharmony_ci * 11462306a36Sopenharmony_ci * If the bus configuration is NOT correct, reading 0xc0c0 asserts 11562306a36Sopenharmony_ci * BE[1:0] and this read returns non-zero 0x8872 value. 11662306a36Sopenharmony_ci */ 11762306a36Sopenharmony_ci iowrite16(BE3 | BE2 | KS_CIDER, ksp->hw_addr_cmd); 11862306a36Sopenharmony_ci cider = ioread16(ksp->hw_addr); 11962306a36Sopenharmony_ci if (!cider) 12062306a36Sopenharmony_ci return 0; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci netdev_err(ks->netdev, "incorrect EESK endian strap setting\n"); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return -EINVAL; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci/** 12862306a36Sopenharmony_ci * ks8851_wrreg16_par - write 16bit register value to chip 12962306a36Sopenharmony_ci * @ks: The chip state 13062306a36Sopenharmony_ci * @reg: The register address 13162306a36Sopenharmony_ci * @val: The value to write 13262306a36Sopenharmony_ci * 13362306a36Sopenharmony_ci * Issue a write to put the value @val into the register specified in @reg. 13462306a36Sopenharmony_ci */ 13562306a36Sopenharmony_cistatic void ks8851_wrreg16_par(struct ks8851_net *ks, unsigned int reg, 13662306a36Sopenharmony_ci unsigned int val) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci struct ks8851_net_par *ksp = to_ks8851_par(ks); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci ksp->cmd_reg_cache = (u16)reg | ((BE1 | BE0) << (reg & 0x02)); 14162306a36Sopenharmony_ci iowrite16(ksp->cmd_reg_cache, ksp->hw_addr_cmd); 14262306a36Sopenharmony_ci iowrite16(val, ksp->hw_addr); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci/** 14662306a36Sopenharmony_ci * ks8851_rdreg16_par - read 16 bit register from chip 14762306a36Sopenharmony_ci * @ks: The chip information 14862306a36Sopenharmony_ci * @reg: The register address 14962306a36Sopenharmony_ci * 15062306a36Sopenharmony_ci * Read a 16bit register from the chip, returning the result 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_cistatic unsigned int ks8851_rdreg16_par(struct ks8851_net *ks, unsigned int reg) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct ks8851_net_par *ksp = to_ks8851_par(ks); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci ksp->cmd_reg_cache = (u16)reg | ((BE1 | BE0) << (reg & 0x02)); 15762306a36Sopenharmony_ci iowrite16(ksp->cmd_reg_cache, ksp->hw_addr_cmd); 15862306a36Sopenharmony_ci return ioread16(ksp->hw_addr); 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci/** 16262306a36Sopenharmony_ci * ks8851_rdfifo_par - read data from the receive fifo 16362306a36Sopenharmony_ci * @ks: The device state. 16462306a36Sopenharmony_ci * @buff: The buffer address 16562306a36Sopenharmony_ci * @len: The length of the data to read 16662306a36Sopenharmony_ci * 16762306a36Sopenharmony_ci * Issue an RXQ FIFO read command and read the @len amount of data from 16862306a36Sopenharmony_ci * the FIFO into the buffer specified by @buff. 16962306a36Sopenharmony_ci */ 17062306a36Sopenharmony_cistatic void ks8851_rdfifo_par(struct ks8851_net *ks, u8 *buff, unsigned int len) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci struct ks8851_net_par *ksp = to_ks8851_par(ks); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci netif_dbg(ks, rx_status, ks->netdev, 17562306a36Sopenharmony_ci "%s: %d@%p\n", __func__, len, buff); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci ioread16_rep(ksp->hw_addr, (u16 *)buff + 1, len / 2); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci/** 18162306a36Sopenharmony_ci * ks8851_wrfifo_par - write packet to TX FIFO 18262306a36Sopenharmony_ci * @ks: The device state. 18362306a36Sopenharmony_ci * @txp: The sk_buff to transmit. 18462306a36Sopenharmony_ci * @irq: IRQ on completion of the packet. 18562306a36Sopenharmony_ci * 18662306a36Sopenharmony_ci * Send the @txp to the chip. This means creating the relevant packet header 18762306a36Sopenharmony_ci * specifying the length of the packet and the other information the chip 18862306a36Sopenharmony_ci * needs, such as IRQ on completion. Send the header and the packet data to 18962306a36Sopenharmony_ci * the device. 19062306a36Sopenharmony_ci */ 19162306a36Sopenharmony_cistatic void ks8851_wrfifo_par(struct ks8851_net *ks, struct sk_buff *txp, 19262306a36Sopenharmony_ci bool irq) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct ks8851_net_par *ksp = to_ks8851_par(ks); 19562306a36Sopenharmony_ci unsigned int len = ALIGN(txp->len, 4); 19662306a36Sopenharmony_ci unsigned int fid = 0; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci netif_dbg(ks, tx_queued, ks->netdev, "%s: skb %p, %d@%p, irq %d\n", 19962306a36Sopenharmony_ci __func__, txp, txp->len, txp->data, irq); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci fid = ks->fid++; 20262306a36Sopenharmony_ci fid &= TXFR_TXFID_MASK; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (irq) 20562306a36Sopenharmony_ci fid |= TXFR_TXIC; /* irq on completion */ 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci iowrite16(fid, ksp->hw_addr); 20862306a36Sopenharmony_ci iowrite16(txp->len, ksp->hw_addr); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci iowrite16_rep(ksp->hw_addr, txp->data, len / 2); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci/** 21462306a36Sopenharmony_ci * ks8851_rx_skb_par - receive skbuff 21562306a36Sopenharmony_ci * @ks: The device state. 21662306a36Sopenharmony_ci * @skb: The skbuff 21762306a36Sopenharmony_ci */ 21862306a36Sopenharmony_cistatic void ks8851_rx_skb_par(struct ks8851_net *ks, struct sk_buff *skb) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci netif_rx(skb); 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic unsigned int ks8851_rdreg16_par_txqcr(struct ks8851_net *ks) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci return ks8851_rdreg16_par(ks, KS_TXQCR); 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci/** 22962306a36Sopenharmony_ci * ks8851_start_xmit_par - transmit packet 23062306a36Sopenharmony_ci * @skb: The buffer to transmit 23162306a36Sopenharmony_ci * @dev: The device used to transmit the packet. 23262306a36Sopenharmony_ci * 23362306a36Sopenharmony_ci * Called by the network layer to transmit the @skb. Queue the packet for 23462306a36Sopenharmony_ci * the device and schedule the necessary work to transmit the packet when 23562306a36Sopenharmony_ci * it is free. 23662306a36Sopenharmony_ci * 23762306a36Sopenharmony_ci * We do this to firstly avoid sleeping with the network device locked, 23862306a36Sopenharmony_ci * and secondly so we can round up more than one packet to transmit which 23962306a36Sopenharmony_ci * means we can try and avoid generating too many transmit done interrupts. 24062306a36Sopenharmony_ci */ 24162306a36Sopenharmony_cistatic netdev_tx_t ks8851_start_xmit_par(struct sk_buff *skb, 24262306a36Sopenharmony_ci struct net_device *dev) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct ks8851_net *ks = netdev_priv(dev); 24562306a36Sopenharmony_ci netdev_tx_t ret = NETDEV_TX_OK; 24662306a36Sopenharmony_ci unsigned long flags; 24762306a36Sopenharmony_ci unsigned int txqcr; 24862306a36Sopenharmony_ci u16 txmir; 24962306a36Sopenharmony_ci int err; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci netif_dbg(ks, tx_queued, ks->netdev, 25262306a36Sopenharmony_ci "%s: skb %p, %d@%p\n", __func__, skb, skb->len, skb->data); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci ks8851_lock_par(ks, &flags); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci txmir = ks8851_rdreg16_par(ks, KS_TXMIR) & 0x1fff; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (likely(txmir >= skb->len + 12)) { 25962306a36Sopenharmony_ci ks8851_wrreg16_par(ks, KS_RXQCR, ks->rc_rxqcr | RXQCR_SDA); 26062306a36Sopenharmony_ci ks8851_wrfifo_par(ks, skb, false); 26162306a36Sopenharmony_ci ks8851_wrreg16_par(ks, KS_RXQCR, ks->rc_rxqcr); 26262306a36Sopenharmony_ci ks8851_wrreg16_par(ks, KS_TXQCR, TXQCR_METFE); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci err = readx_poll_timeout_atomic(ks8851_rdreg16_par_txqcr, ks, 26562306a36Sopenharmony_ci txqcr, !(txqcr & TXQCR_METFE), 26662306a36Sopenharmony_ci 5, 1000000); 26762306a36Sopenharmony_ci if (err) 26862306a36Sopenharmony_ci ret = NETDEV_TX_BUSY; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci ks8851_done_tx(ks, skb); 27162306a36Sopenharmony_ci } else { 27262306a36Sopenharmony_ci ret = NETDEV_TX_BUSY; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci ks8851_unlock_par(ks, &flags); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci return ret; 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic int ks8851_probe_par(struct platform_device *pdev) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct device *dev = &pdev->dev; 28362306a36Sopenharmony_ci struct ks8851_net_par *ksp; 28462306a36Sopenharmony_ci struct net_device *netdev; 28562306a36Sopenharmony_ci struct ks8851_net *ks; 28662306a36Sopenharmony_ci int ret; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci netdev = devm_alloc_etherdev(dev, sizeof(struct ks8851_net_par)); 28962306a36Sopenharmony_ci if (!netdev) 29062306a36Sopenharmony_ci return -ENOMEM; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci ks = netdev_priv(netdev); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci ks->lock = ks8851_lock_par; 29562306a36Sopenharmony_ci ks->unlock = ks8851_unlock_par; 29662306a36Sopenharmony_ci ks->rdreg16 = ks8851_rdreg16_par; 29762306a36Sopenharmony_ci ks->wrreg16 = ks8851_wrreg16_par; 29862306a36Sopenharmony_ci ks->rdfifo = ks8851_rdfifo_par; 29962306a36Sopenharmony_ci ks->wrfifo = ks8851_wrfifo_par; 30062306a36Sopenharmony_ci ks->start_xmit = ks8851_start_xmit_par; 30162306a36Sopenharmony_ci ks->rx_skb = ks8851_rx_skb_par; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci#define STD_IRQ (IRQ_LCI | /* Link Change */ \ 30462306a36Sopenharmony_ci IRQ_RXI | /* RX done */ \ 30562306a36Sopenharmony_ci IRQ_RXPSI) /* RX process stop */ 30662306a36Sopenharmony_ci ks->rc_ier = STD_IRQ; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci ksp = to_ks8851_par(ks); 30962306a36Sopenharmony_ci spin_lock_init(&ksp->lock); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci ksp->hw_addr = devm_platform_ioremap_resource(pdev, 0); 31262306a36Sopenharmony_ci if (IS_ERR(ksp->hw_addr)) 31362306a36Sopenharmony_ci return PTR_ERR(ksp->hw_addr); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci ksp->hw_addr_cmd = devm_platform_ioremap_resource(pdev, 1); 31662306a36Sopenharmony_ci if (IS_ERR(ksp->hw_addr_cmd)) 31762306a36Sopenharmony_ci return PTR_ERR(ksp->hw_addr_cmd); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci ret = ks_check_endian(ks); 32062306a36Sopenharmony_ci if (ret) 32162306a36Sopenharmony_ci return ret; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci netdev->irq = platform_get_irq(pdev, 0); 32462306a36Sopenharmony_ci if (netdev->irq < 0) 32562306a36Sopenharmony_ci return netdev->irq; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci return ks8851_probe_common(netdev, dev, msg_enable); 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic int ks8851_remove_par(struct platform_device *pdev) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci ks8851_remove_common(&pdev->dev); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci return 0; 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic const struct of_device_id ks8851_match_table[] = { 33862306a36Sopenharmony_ci { .compatible = "micrel,ks8851-mll" }, 33962306a36Sopenharmony_ci { } 34062306a36Sopenharmony_ci}; 34162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ks8851_match_table); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cistatic struct platform_driver ks8851_driver = { 34462306a36Sopenharmony_ci .driver = { 34562306a36Sopenharmony_ci .name = "ks8851", 34662306a36Sopenharmony_ci .of_match_table = ks8851_match_table, 34762306a36Sopenharmony_ci .pm = &ks8851_pm_ops, 34862306a36Sopenharmony_ci }, 34962306a36Sopenharmony_ci .probe = ks8851_probe_par, 35062306a36Sopenharmony_ci .remove = ks8851_remove_par, 35162306a36Sopenharmony_ci}; 35262306a36Sopenharmony_cimodule_platform_driver(ks8851_driver); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ciMODULE_DESCRIPTION("KS8851 Network driver"); 35562306a36Sopenharmony_ciMODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); 35662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cimodule_param_named(message, msg_enable, int, 0); 35962306a36Sopenharmony_ciMODULE_PARM_DESC(message, "Message verbosity level (0=none, 31=all)"); 360