Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/media/pci/netup_unidvb/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 9 kB image not shown  

Quelle  netup_unidvb_i2c.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * netup_unidvb_i2c.c
 *
 * Internal I2C bus driver for NetUP Universal Dual DVB-CI
 *
 * Copyright (C) 2014 NetUP Inc.
 * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
 * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru>
 */


#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include "netup_unidvb.h"

#define NETUP_I2C_BUS0_ADDR  0x4800
#define NETUP_I2C_BUS1_ADDR  0x4840
#define NETUP_I2C_TIMEOUT  1000

/* twi_ctrl0_stat reg bits */
#define TWI_IRQEN_COMPL 0x1
#define TWI_IRQEN_ANACK 0x2
#define TWI_IRQEN_DNACK 0x4
#define TWI_IRQ_COMPL (TWI_IRQEN_COMPL << 8)
#define TWI_IRQ_ANACK (TWI_IRQEN_ANACK << 8)
#define TWI_IRQ_DNACK (TWI_IRQEN_DNACK << 8)
#define TWI_IRQ_TX 0x800
#define TWI_IRQ_RX 0x1000
#define TWI_IRQEN (TWI_IRQEN_COMPL | TWI_IRQEN_ANACK | TWI_IRQEN_DNACK)
/* twi_addr_ctrl1 reg bits*/
#define TWI_TRANSFER 0x100
#define TWI_NOSTOP 0x200
#define TWI_SOFT_RESET 0x2000
/* twi_clkdiv reg value */
#define TWI_CLKDIV 156
/* fifo_stat_ctrl reg bits */
#define FIFO_IRQEN 0x8000
#define FIFO_RESET 0x4000
/* FIFO size */
#define FIFO_SIZE 16

struct netup_i2c_fifo_regs {
 union {
  __u8 data8;
  __le16 data16;
  __le32 data32;
 };
 __u8  padding[4];
 __le16  stat_ctrl;
} __packed __aligned(1);

struct netup_i2c_regs {
 __le16    clkdiv;
 __le16    twi_ctrl0_stat;
 __le16    twi_addr_ctrl1;
 __le16    length;
 __u8    padding1[8];
 struct netup_i2c_fifo_regs tx_fifo;
 __u8    padding2[6];
 struct netup_i2c_fifo_regs rx_fifo;
} __packed __aligned(1);

irqreturn_t netup_i2c_interrupt(struct netup_i2c *i2c)
{
 u16 reg, tmp;
 unsigned long flags;
 irqreturn_t iret = IRQ_HANDLED;

 spin_lock_irqsave(&i2c->lock, flags);
 reg = readw(&i2c->regs->twi_ctrl0_stat);
 writew(reg & ~TWI_IRQEN, &i2c->regs->twi_ctrl0_stat);
 dev_dbg(i2c->adap.dev.parent,
  "%s(): twi_ctrl0_state 0x%x\n", __func__, reg);
 if ((reg & TWI_IRQEN_COMPL) != 0 && (reg & TWI_IRQ_COMPL)) {
  dev_dbg(i2c->adap.dev.parent,
   "%s(): TWI_IRQEN_COMPL\n", __func__);
  i2c->state = STATE_DONE;
  goto irq_ok;
 }
 if ((reg & TWI_IRQEN_ANACK) != 0 && (reg & TWI_IRQ_ANACK)) {
  dev_dbg(i2c->adap.dev.parent,
   "%s(): TWI_IRQEN_ANACK\n", __func__);
  i2c->state = STATE_ERROR;
  goto irq_ok;
 }
 if ((reg & TWI_IRQEN_DNACK) != 0 && (reg & TWI_IRQ_DNACK)) {
  dev_dbg(i2c->adap.dev.parent,
   "%s(): TWI_IRQEN_DNACK\n", __func__);
  i2c->state = STATE_ERROR;
  goto irq_ok;
 }
 if ((reg & TWI_IRQ_RX) != 0) {
  tmp = readw(&i2c->regs->rx_fifo.stat_ctrl);
  writew(tmp & ~FIFO_IRQEN, &i2c->regs->rx_fifo.stat_ctrl);
  i2c->state = STATE_WANT_READ;
  dev_dbg(i2c->adap.dev.parent,
   "%s(): want read\n", __func__);
  goto irq_ok;
 }
 if ((reg & TWI_IRQ_TX) != 0) {
  tmp = readw(&i2c->regs->tx_fifo.stat_ctrl);
  writew(tmp & ~FIFO_IRQEN, &i2c->regs->tx_fifo.stat_ctrl);
  i2c->state = STATE_WANT_WRITE;
  dev_dbg(i2c->adap.dev.parent,
   "%s(): want write\n", __func__);
  goto irq_ok;
 }
 dev_warn(&i2c->adap.dev, "%s(): not mine interrupt\n", __func__);
 iret = IRQ_NONE;
irq_ok:
 spin_unlock_irqrestore(&i2c->lock, flags);
 if (iret == IRQ_HANDLED)
  wake_up(&i2c->wq);
 return iret;
}

static void netup_i2c_reset(struct netup_i2c *i2c)
{
 dev_dbg(i2c->adap.dev.parent, "%s()\n", __func__);
 i2c->state = STATE_DONE;
 writew(TWI_SOFT_RESET, &i2c->regs->twi_addr_ctrl1);
 writew(TWI_CLKDIV, &i2c->regs->clkdiv);
 writew(FIFO_RESET, &i2c->regs->tx_fifo.stat_ctrl);
 writew(FIFO_RESET, &i2c->regs->rx_fifo.stat_ctrl);
 writew(0x800, &i2c->regs->tx_fifo.stat_ctrl);
 writew(0x800, &i2c->regs->rx_fifo.stat_ctrl);
}

static void netup_i2c_fifo_tx(struct netup_i2c *i2c)
{
 u8 data;
 u32 fifo_space = FIFO_SIZE -
  (readw(&i2c->regs->tx_fifo.stat_ctrl) & 0x3f);
 u32 msg_length = i2c->msg->len - i2c->xmit_size;

 msg_length = min(msg_length, fifo_space);
 while (msg_length--) {
  data = i2c->msg->buf[i2c->xmit_size++];
  writeb(data, &i2c->regs->tx_fifo.data8);
  dev_dbg(i2c->adap.dev.parent,
   "%s(): write 0x%02x\n", __func__, data);
 }
 if (i2c->xmit_size < i2c->msg->len) {
  dev_dbg(i2c->adap.dev.parent,
   "%s(): TX IRQ enabled\n", __func__);
  writew(readw(&i2c->regs->tx_fifo.stat_ctrl) | FIFO_IRQEN,
   &i2c->regs->tx_fifo.stat_ctrl);
 }
}

static void netup_i2c_fifo_rx(struct netup_i2c *i2c)
{
 u8 data;
 u32 fifo_size = readw(&i2c->regs->rx_fifo.stat_ctrl) & 0x3f;

 dev_dbg(i2c->adap.dev.parent,
  "%s(): RX fifo size %d\n", __func__, fifo_size);
 while (fifo_size--) {
  data = readb(&i2c->regs->rx_fifo.data8);
  if ((i2c->msg->flags & I2C_M_RD) != 0 &&
     i2c->xmit_size < i2c->msg->len) {
   i2c->msg->buf[i2c->xmit_size++] = data;
   dev_dbg(i2c->adap.dev.parent,
    "%s(): read 0x%02x\n", __func__, data);
  }
 }
 if (i2c->xmit_size < i2c->msg->len) {
  dev_dbg(i2c->adap.dev.parent,
   "%s(): RX IRQ enabled\n", __func__);
  writew(readw(&i2c->regs->rx_fifo.stat_ctrl) | FIFO_IRQEN,
   &i2c->regs->rx_fifo.stat_ctrl);
 }
}

static void netup_i2c_start_xfer(struct netup_i2c *i2c)
{
 u16 rdflag = ((i2c->msg->flags & I2C_M_RD) ? 1 : 0);
 u16 reg = readw(&i2c->regs->twi_ctrl0_stat);

 writew(TWI_IRQEN | reg, &i2c->regs->twi_ctrl0_stat);
 writew(i2c->msg->len, &i2c->regs->length);
 writew(TWI_TRANSFER | (i2c->msg->addr << 1) | rdflag,
  &i2c->regs->twi_addr_ctrl1);
 dev_dbg(i2c->adap.dev.parent,
  "%s(): length %d twi_addr_ctrl1 0x%x twi_ctrl0_stat 0x%x\n",
  __func__, readw(&i2c->regs->length),
  readw(&i2c->regs->twi_addr_ctrl1),
  readw(&i2c->regs->twi_ctrl0_stat));
 i2c->state = STATE_WAIT;
 i2c->xmit_size = 0;
 if (!rdflag)
  netup_i2c_fifo_tx(i2c);
 else
  writew(FIFO_IRQEN | readw(&i2c->regs->rx_fifo.stat_ctrl),
   &i2c->regs->rx_fifo.stat_ctrl);
}

static int netup_i2c_xfer(struct i2c_adapter *adap,
     struct i2c_msg *msgs, int num)
{
 unsigned long flags;
 int i, trans_done, res = num;
 struct netup_i2c *i2c = i2c_get_adapdata(adap);
 u16 reg;

 spin_lock_irqsave(&i2c->lock, flags);
 if (i2c->state != STATE_DONE) {
  dev_dbg(i2c->adap.dev.parent,
   "%s(): i2c->state == %d, resetting I2C\n",
   __func__, i2c->state);
  netup_i2c_reset(i2c);
 }
 dev_dbg(i2c->adap.dev.parent, "%s() num %d\n", __func__, num);
 for (i = 0; i < num; i++) {
  i2c->msg = &msgs[i];
  netup_i2c_start_xfer(i2c);
  trans_done = 0;
  while (!trans_done) {
   spin_unlock_irqrestore(&i2c->lock, flags);
   if (wait_event_timeout(i2c->wq,
     i2c->state != STATE_WAIT,
     msecs_to_jiffies(NETUP_I2C_TIMEOUT))) {
    spin_lock_irqsave(&i2c->lock, flags);
    switch (i2c->state) {
    case STATE_WANT_READ:
     netup_i2c_fifo_rx(i2c);
     break;
    case STATE_WANT_WRITE:
     netup_i2c_fifo_tx(i2c);
     break;
    case STATE_DONE:
     if ((i2c->msg->flags & I2C_M_RD) != 0 &&
      i2c->xmit_size != i2c->msg->len)
      netup_i2c_fifo_rx(i2c);
     dev_dbg(i2c->adap.dev.parent,
      "%s(): msg %d OK\n",
      __func__, i);
     trans_done = 1;
     break;
    case STATE_ERROR:
     res = -EIO;
     dev_dbg(i2c->adap.dev.parent,
      "%s(): error state\n",
      __func__);
     goto done;
    default:
     dev_dbg(i2c->adap.dev.parent,
      "%s(): invalid state %d\n",
      __func__, i2c->state);
     res = -EINVAL;
     goto done;
    }
    if (!trans_done) {
     i2c->state = STATE_WAIT;
     reg = readw(
      &i2c->regs->twi_ctrl0_stat);
     writew(TWI_IRQEN | reg,
      &i2c->regs->twi_ctrl0_stat);
    }
    spin_unlock_irqrestore(&i2c->lock, flags);
   } else {
    spin_lock_irqsave(&i2c->lock, flags);
    dev_dbg(i2c->adap.dev.parent,
     "%s(): wait timeout\n", __func__);
    res = -ETIMEDOUT;
    goto done;
   }
   spin_lock_irqsave(&i2c->lock, flags);
  }
 }
done:
 spin_unlock_irqrestore(&i2c->lock, flags);
 dev_dbg(i2c->adap.dev.parent, "%s(): result %d\n", __func__, res);
 return res;
}

static u32 netup_i2c_func(struct i2c_adapter *adap)
{
 return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}

static const struct i2c_algorithm netup_i2c_algorithm = {
 .master_xfer = netup_i2c_xfer,
 .functionality = netup_i2c_func,
};

static const struct i2c_adapter netup_i2c_adapter = {
 .owner  = THIS_MODULE,
 .name  = NETUP_UNIDVB_NAME,
 .class  = I2C_CLASS_HWMON,
 .algo  = &netup_i2c_algorithm,
};

static int netup_i2c_init(struct netup_unidvb_dev *ndev, int bus_num)
{
 int ret;
 struct netup_i2c *i2c;

 if (bus_num < 0 || bus_num > 1) {
  dev_err(&ndev->pci_dev->dev,
   "%s(): invalid bus_num %d\n", __func__, bus_num);
  return -EINVAL;
 }
 i2c = &ndev->i2c[bus_num];
 spin_lock_init(&i2c->lock);
 init_waitqueue_head(&i2c->wq);
 i2c->regs = (struct netup_i2c_regs __iomem *)(ndev->bmmio0 +
  (bus_num == 0 ? NETUP_I2C_BUS0_ADDR : NETUP_I2C_BUS1_ADDR));
 netup_i2c_reset(i2c);
 i2c->adap = netup_i2c_adapter;
 i2c->adap.dev.parent = &ndev->pci_dev->dev;
 i2c_set_adapdata(&i2c->adap, i2c);
 ret = i2c_add_adapter(&i2c->adap);
 if (ret)
  return ret;
 dev_info(&ndev->pci_dev->dev,
  "%s(): registered I2C bus %d at 0x%x\n",
  __func__,
  bus_num, (bus_num == 0 ?
   NETUP_I2C_BUS0_ADDR :
   NETUP_I2C_BUS1_ADDR));
 return 0;
}

static void netup_i2c_remove(struct netup_unidvb_dev *ndev, int bus_num)
{
 struct netup_i2c *i2c;

 if (bus_num < 0 || bus_num > 1) {
  dev_err(&ndev->pci_dev->dev,
   "%s(): invalid bus number %d\n", __func__, bus_num);
  return;
 }
 i2c = &ndev->i2c[bus_num];
 netup_i2c_reset(i2c);
 /* remove adapter */
 i2c_del_adapter(&i2c->adap);
 dev_info(&ndev->pci_dev->dev,
  "netup_i2c_remove: unregistered I2C bus %d\n", bus_num);
}

int netup_i2c_register(struct netup_unidvb_dev *ndev)
{
 int ret;

 ret = netup_i2c_init(ndev, 0);
 if (ret)
  return ret;
 ret = netup_i2c_init(ndev, 1);
 if (ret) {
  netup_i2c_remove(ndev, 0);
  return ret;
 }
 return 0;
}

void netup_i2c_unregister(struct netup_unidvb_dev *ndev)
{
 netup_i2c_remove(ndev, 0);
 netup_i2c_remove(ndev, 1);
}


Messung V0.5
C=98 H=92 G=94

¤ Dauer der Verarbeitung: 0.18 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.