Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Subhendu Sekhar Behera | 1886 | 72.29% | 1 | 5.88% |
George Cherian | 454 | 17.40% | 8 | 47.06% |
Kamlakant Patel | 118 | 4.52% | 1 | 5.88% |
Jayachandran C | 73 | 2.80% | 2 | 11.76% |
Tanmay Jagdale | 63 | 2.41% | 2 | 11.76% |
Javier Martinez Canillas | 7 | 0.27% | 1 | 5.88% |
Dmitry Bazhenov | 7 | 0.27% | 1 | 5.88% |
Bhumika Goyal | 1 | 0.04% | 1 | 5.88% |
Total | 2609 | 17 |
/* * Copyright (c) 2003-2015 Broadcom Corporation * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied. */ #include <linux/acpi.h> #include <linux/clk.h> #include <linux/completion.h> #include <linux/i2c.h> #include <linux/i2c-smbus.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/delay.h> #define XLP9XX_I2C_DIV 0x0 #define XLP9XX_I2C_CTRL 0x1 #define XLP9XX_I2C_CMD 0x2 #define XLP9XX_I2C_STATUS 0x3 #define XLP9XX_I2C_MTXFIFO 0x4 #define XLP9XX_I2C_MRXFIFO 0x5 #define XLP9XX_I2C_MFIFOCTRL 0x6 #define XLP9XX_I2C_STXFIFO 0x7 #define XLP9XX_I2C_SRXFIFO 0x8 #define XLP9XX_I2C_SFIFOCTRL 0x9 #define XLP9XX_I2C_SLAVEADDR 0xA #define XLP9XX_I2C_OWNADDR 0xB #define XLP9XX_I2C_FIFOWCNT 0xC #define XLP9XX_I2C_INTEN 0xD #define XLP9XX_I2C_INTST 0xE #define XLP9XX_I2C_WAITCNT 0xF #define XLP9XX_I2C_TIMEOUT 0X10 #define XLP9XX_I2C_GENCALLADDR 0x11 #define XLP9XX_I2C_STATUS_BUSY BIT(0) #define XLP9XX_I2C_CMD_START BIT(7) #define XLP9XX_I2C_CMD_STOP BIT(6) #define XLP9XX_I2C_CMD_READ BIT(5) #define XLP9XX_I2C_CMD_WRITE BIT(4) #define XLP9XX_I2C_CMD_ACK BIT(3) #define XLP9XX_I2C_CTRL_MCTLEN_SHIFT 16 #define XLP9XX_I2C_CTRL_MCTLEN_MASK 0xffff0000 #define XLP9XX_I2C_CTRL_RST BIT(8) #define XLP9XX_I2C_CTRL_EN BIT(6) #define XLP9XX_I2C_CTRL_MASTER BIT(4) #define XLP9XX_I2C_CTRL_FIFORD BIT(1) #define XLP9XX_I2C_CTRL_ADDMODE BIT(0) #define XLP9XX_I2C_INTEN_NACKADDR BIT(25) #define XLP9XX_I2C_INTEN_SADDR BIT(13) #define XLP9XX_I2C_INTEN_DATADONE BIT(12) #define XLP9XX_I2C_INTEN_ARLOST BIT(11) #define XLP9XX_I2C_INTEN_MFIFOFULL BIT(4) #define XLP9XX_I2C_INTEN_MFIFOEMTY BIT(3) #define XLP9XX_I2C_INTEN_MFIFOHI BIT(2) #define XLP9XX_I2C_INTEN_BUSERR BIT(0) #define XLP9XX_I2C_MFIFOCTRL_HITH_SHIFT 8 #define XLP9XX_I2C_MFIFOCTRL_LOTH_SHIFT 0 #define XLP9XX_I2C_MFIFOCTRL_RST BIT(16) #define XLP9XX_I2C_SLAVEADDR_RW BIT(0) #define XLP9XX_I2C_SLAVEADDR_ADDR_SHIFT 1 #define XLP9XX_I2C_IP_CLK_FREQ 133000000UL #define XLP9XX_I2C_DEFAULT_FREQ 100000 #define XLP9XX_I2C_HIGH_FREQ 400000 #define XLP9XX_I2C_FIFO_SIZE 0x80U #define XLP9XX_I2C_TIMEOUT_MS 1000 #define XLP9XX_I2C_BUSY_TIMEOUT 50 #define XLP9XX_I2C_FIFO_WCNT_MASK 0xff #define XLP9XX_I2C_STATUS_ERRMASK (XLP9XX_I2C_INTEN_ARLOST | \ XLP9XX_I2C_INTEN_NACKADDR | XLP9XX_I2C_INTEN_BUSERR) struct xlp9xx_i2c_dev { struct device *dev; struct i2c_adapter adapter; struct completion msg_complete; struct i2c_smbus_alert_setup alert_data; struct i2c_client *ara; int irq; bool msg_read; bool len_recv; bool client_pec; u32 __iomem *base; u32 msg_buf_remaining; u32 msg_len; u32 ip_clk_hz; u32 clk_hz; u32 msg_err; u8 *msg_buf; }; static inline void xlp9xx_write_i2c_reg(struct xlp9xx_i2c_dev *priv, unsigned long reg, u32 val) { writel(val, priv->base + reg); } static inline u32 xlp9xx_read_i2c_reg(struct xlp9xx_i2c_dev *priv, unsigned long reg) { return readl(priv->base + reg); } static void xlp9xx_i2c_mask_irq(struct xlp9xx_i2c_dev *priv, u32 mask) { u32 inten; inten = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_INTEN) & ~mask; xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_INTEN, inten); } static void xlp9xx_i2c_unmask_irq(struct xlp9xx_i2c_dev *priv, u32 mask) { u32 inten; inten = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_INTEN) | mask; xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_INTEN, inten); } static void xlp9xx_i2c_update_rx_fifo_thres(struct xlp9xx_i2c_dev *priv) { u32 thres; if (priv->len_recv) /* interrupt after the first read to examine * the length byte before proceeding further */ thres = 1; else if (priv->msg_buf_remaining > XLP9XX_I2C_FIFO_SIZE) thres = XLP9XX_I2C_FIFO_SIZE; else thres = priv->msg_buf_remaining; xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_MFIFOCTRL, thres << XLP9XX_I2C_MFIFOCTRL_HITH_SHIFT); } static void xlp9xx_i2c_fill_tx_fifo(struct xlp9xx_i2c_dev *priv) { u32 len, i; u8 *buf = priv->msg_buf; len = min(priv->msg_buf_remaining, XLP9XX_I2C_FIFO_SIZE); for (i = 0; i < len; i++) xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_MTXFIFO, buf[i]); priv->msg_buf_remaining -= len; priv->msg_buf += len; } static void xlp9xx_i2c_update_rlen(struct xlp9xx_i2c_dev *priv) { u32 val, len; /* * Update receive length. Re-read len to get the latest value, * and then add 4 to have a minimum value that can be safely * written. This is to account for the byte read above, the * transfer in progress and any delays in the register I/O */ val = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_CTRL); len = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_FIFOWCNT) & XLP9XX_I2C_FIFO_WCNT_MASK; len = max_t(u32, priv->msg_len, len + 4); if (len >= I2C_SMBUS_BLOCK_MAX + 2) return; val = (val & ~XLP9XX_I2C_CTRL_MCTLEN_MASK) | (len << XLP9XX_I2C_CTRL_MCTLEN_SHIFT); xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CTRL, val); } static void xlp9xx_i2c_drain_rx_fifo(struct xlp9xx_i2c_dev *priv) { u32 len, i; u8 rlen, *buf = priv->msg_buf; len = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_FIFOWCNT) & XLP9XX_I2C_FIFO_WCNT_MASK; if (!len) return; if (priv->len_recv) { /* read length byte */ rlen = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_MRXFIFO); /* * We expect at least 2 interrupts for I2C_M_RECV_LEN * transactions. The length is updated during the first * interrupt, and the buffer contents are only copied * during subsequent interrupts. If in case the interrupts * get merged we would complete the transaction without * copying out the bytes from RX fifo. To avoid this now we * drain the fifo as and when data is available. * We drained the rlen byte already, decrement total length * by one. */ len--; if (rlen > I2C_SMBUS_BLOCK_MAX || rlen == 0) { rlen = 0; /*abort transfer */ priv->msg_buf_remaining = 0; priv->msg_len = 0; xlp9xx_i2c_update_rlen(priv); return; } *buf++ = rlen; if (priv->client_pec) ++rlen; /* account for error check byte */ /* update remaining bytes and message length */ priv->msg_buf_remaining = rlen; priv->msg_len = rlen + 1; xlp9xx_i2c_update_rlen(priv); priv->len_recv = false; } len = min(priv->msg_buf_remaining, len); for (i = 0; i < len; i++, buf++) *buf = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_MRXFIFO); priv->msg_buf_remaining -= len; priv->msg_buf = buf; if (priv->msg_buf_remaining) xlp9xx_i2c_update_rx_fifo_thres(priv); } static irqreturn_t xlp9xx_i2c_isr(int irq, void *dev_id) { struct xlp9xx_i2c_dev *priv = dev_id; u32 status; status = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_INTST); if (status == 0) return IRQ_NONE; xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_INTST, status); if (status & XLP9XX_I2C_STATUS_ERRMASK) { priv->msg_err = status; goto xfer_done; } /* SADDR ACK for SMBUS_QUICK */ if ((status & XLP9XX_I2C_INTEN_SADDR) && (priv->msg_len == 0)) goto xfer_done; if (!priv->msg_read) { if (status & XLP9XX_I2C_INTEN_MFIFOEMTY) { /* TX FIFO got empty, fill it up again */ if (priv->msg_buf_remaining) xlp9xx_i2c_fill_tx_fifo(priv); else xlp9xx_i2c_mask_irq(priv, XLP9XX_I2C_INTEN_MFIFOEMTY); } } else { if (status & (XLP9XX_I2C_INTEN_DATADONE | XLP9XX_I2C_INTEN_MFIFOHI)) { /* data is in FIFO, read it */ if (priv->msg_buf_remaining) xlp9xx_i2c_drain_rx_fifo(priv); } } /* Transfer complete */ if (status & XLP9XX_I2C_INTEN_DATADONE) goto xfer_done; return IRQ_HANDLED; xfer_done: xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_INTEN, 0); complete(&priv->msg_complete); return IRQ_HANDLED; } static int xlp9xx_i2c_check_bus_status(struct xlp9xx_i2c_dev *priv) { u32 status; u32 busy_timeout = XLP9XX_I2C_BUSY_TIMEOUT; while (busy_timeout) { status = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_STATUS); if ((status & XLP9XX_I2C_STATUS_BUSY) == 0) break; busy_timeout--; usleep_range(1000, 1100); } if (!busy_timeout) return -EIO; return 0; } static int xlp9xx_i2c_init(struct xlp9xx_i2c_dev *priv) { u32 prescale; /* * The controller uses 5 * SCL clock internally. * So prescale value should be divided by 5. */ prescale = DIV_ROUND_UP(priv->ip_clk_hz, priv->clk_hz); prescale = ((prescale - 8) / 5) - 1; xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CTRL, XLP9XX_I2C_CTRL_RST); xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CTRL, XLP9XX_I2C_CTRL_EN | XLP9XX_I2C_CTRL_MASTER); xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_DIV, prescale); xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_INTEN, 0); return 0; } static int xlp9xx_i2c_xfer_msg(struct xlp9xx_i2c_dev *priv, struct i2c_msg *msg, int last_msg) { unsigned long timeleft; u32 intr_mask, cmd, val, len; priv->msg_buf = msg->buf; priv->msg_buf_remaining = priv->msg_len = msg->len; priv->msg_err = 0; priv->msg_read = (msg->flags & I2C_M_RD); reinit_completion(&priv->msg_complete); /* Reset FIFO */ xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_MFIFOCTRL, XLP9XX_I2C_MFIFOCTRL_RST); /* set slave addr */ xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_SLAVEADDR, (msg->addr << XLP9XX_I2C_SLAVEADDR_ADDR_SHIFT) | (priv->msg_read ? XLP9XX_I2C_SLAVEADDR_RW : 0)); /* Build control word for transfer */ val = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_CTRL); if (!priv->msg_read) val &= ~XLP9XX_I2C_CTRL_FIFORD; else val |= XLP9XX_I2C_CTRL_FIFORD; /* read */ if (msg->flags & I2C_M_TEN) val |= XLP9XX_I2C_CTRL_ADDMODE; /* 10-bit address mode*/ else val &= ~XLP9XX_I2C_CTRL_ADDMODE; priv->len_recv = msg->flags & I2C_M_RECV_LEN; len = priv->len_recv ? I2C_SMBUS_BLOCK_MAX + 2 : msg->len; priv->client_pec = msg->flags & I2C_CLIENT_PEC; /* set FIFO threshold if reading */ if (priv->msg_read) xlp9xx_i2c_update_rx_fifo_thres(priv); /* set data length to be transferred */ val = (val & ~XLP9XX_I2C_CTRL_MCTLEN_MASK) | (len << XLP9XX_I2C_CTRL_MCTLEN_SHIFT); xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CTRL, val); /* fill fifo during tx */ if (!priv->msg_read) xlp9xx_i2c_fill_tx_fifo(priv); /* set interrupt mask */ intr_mask = (XLP9XX_I2C_INTEN_ARLOST | XLP9XX_I2C_INTEN_BUSERR | XLP9XX_I2C_INTEN_NACKADDR | XLP9XX_I2C_INTEN_DATADONE); if (priv->msg_read) { intr_mask |= XLP9XX_I2C_INTEN_MFIFOHI; if (msg->len == 0) intr_mask |= XLP9XX_I2C_INTEN_SADDR; } else { if (msg->len == 0) intr_mask |= XLP9XX_I2C_INTEN_SADDR; else intr_mask |= XLP9XX_I2C_INTEN_MFIFOEMTY; } xlp9xx_i2c_unmask_irq(priv, intr_mask); /* set cmd reg */ cmd = XLP9XX_I2C_CMD_START; if (msg->len) cmd |= (priv->msg_read ? XLP9XX_I2C_CMD_READ : XLP9XX_I2C_CMD_WRITE); if (last_msg) cmd |= XLP9XX_I2C_CMD_STOP; xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CMD, cmd); timeleft = msecs_to_jiffies(XLP9XX_I2C_TIMEOUT_MS); timeleft = wait_for_completion_timeout(&priv->msg_complete, timeleft); if (priv->msg_err & XLP9XX_I2C_INTEN_BUSERR) { dev_dbg(priv->dev, "transfer error %x!\n", priv->msg_err); xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CMD, XLP9XX_I2C_CMD_STOP); return -EIO; } else if (priv->msg_err & XLP9XX_I2C_INTEN_NACKADDR) { return -ENXIO; } if (timeleft == 0) { dev_dbg(priv->dev, "i2c transfer timed out!\n"); xlp9xx_i2c_init(priv); return -ETIMEDOUT; } /* update msg->len with actual received length */ if (msg->flags & I2C_M_RECV_LEN) { if (!priv->msg_len) return -EPROTO; msg->len = priv->msg_len; } return 0; } static int xlp9xx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { int i, ret; struct xlp9xx_i2c_dev *priv = i2c_get_adapdata(adap); ret = xlp9xx_i2c_check_bus_status(priv); if (ret) { xlp9xx_i2c_init(priv); ret = xlp9xx_i2c_check_bus_status(priv); if (ret) return ret; } for (i = 0; i < num; i++) { ret = xlp9xx_i2c_xfer_msg(priv, &msgs[i], i == num - 1); if (ret != 0) return ret; } return num; } static u32 xlp9xx_i2c_functionality(struct i2c_adapter *adapter) { return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR; } static const struct i2c_algorithm xlp9xx_i2c_algo = { .master_xfer = xlp9xx_i2c_xfer, .functionality = xlp9xx_i2c_functionality, }; static int xlp9xx_i2c_get_frequency(struct platform_device *pdev, struct xlp9xx_i2c_dev *priv) { struct clk *clk; u32 freq; int err; clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(clk)) { priv->ip_clk_hz = XLP9XX_I2C_IP_CLK_FREQ; dev_dbg(&pdev->dev, "using default input frequency %u\n", priv->ip_clk_hz); } else { priv->ip_clk_hz = clk_get_rate(clk); } err = device_property_read_u32(&pdev->dev, "clock-frequency", &freq); if (err) { freq = XLP9XX_I2C_DEFAULT_FREQ; dev_dbg(&pdev->dev, "using default frequency %u\n", freq); } else if (freq == 0 || freq > XLP9XX_I2C_HIGH_FREQ) { dev_warn(&pdev->dev, "invalid frequency %u, using default\n", freq); freq = XLP9XX_I2C_DEFAULT_FREQ; } priv->clk_hz = freq; return 0; } static int xlp9xx_i2c_smbus_setup(struct xlp9xx_i2c_dev *priv, struct platform_device *pdev) { if (!priv->alert_data.irq) return -EINVAL; priv->ara = i2c_setup_smbus_alert(&priv->adapter, &priv->alert_data); if (!priv->ara) return -ENODEV; return 0; } static int xlp9xx_i2c_probe(struct platform_device *pdev) { struct xlp9xx_i2c_dev *priv; struct resource *res; int err = 0; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); priv->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); priv->irq = platform_get_irq(pdev, 0); if (priv->irq <= 0) { dev_err(&pdev->dev, "invalid irq!\n"); return priv->irq; } /* SMBAlert irq */ priv->alert_data.irq = platform_get_irq(pdev, 1); if (priv->alert_data.irq <= 0) priv->alert_data.irq = 0; xlp9xx_i2c_get_frequency(pdev, priv); xlp9xx_i2c_init(priv); err = devm_request_irq(&pdev->dev, priv->irq, xlp9xx_i2c_isr, 0, pdev->name, priv); if (err) { dev_err(&pdev->dev, "IRQ request failed!\n"); return err; } init_completion(&priv->msg_complete); priv->adapter.dev.parent = &pdev->dev; priv->adapter.algo = &xlp9xx_i2c_algo; priv->adapter.class = I2C_CLASS_HWMON; ACPI_COMPANION_SET(&priv->adapter.dev, ACPI_COMPANION(&pdev->dev)); priv->adapter.dev.of_node = pdev->dev.of_node; priv->dev = &pdev->dev; snprintf(priv->adapter.name, sizeof(priv->adapter.name), "xlp9xx-i2c"); i2c_set_adapdata(&priv->adapter, priv); err = i2c_add_adapter(&priv->adapter); if (err) return err; err = xlp9xx_i2c_smbus_setup(priv, pdev); if (err) dev_dbg(&pdev->dev, "No active SMBus alert %d\n", err); platform_set_drvdata(pdev, priv); dev_dbg(&pdev->dev, "I2C bus:%d added\n", priv->adapter.nr); return 0; } static int xlp9xx_i2c_remove(struct platform_device *pdev) { struct xlp9xx_i2c_dev *priv; priv = platform_get_drvdata(pdev); xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_INTEN, 0); synchronize_irq(priv->irq); i2c_del_adapter(&priv->adapter); xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CTRL, 0); return 0; } static const struct of_device_id xlp9xx_i2c_of_match[] = { { .compatible = "netlogic,xlp980-i2c", }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, xlp9xx_i2c_of_match); #ifdef CONFIG_ACPI static const struct acpi_device_id xlp9xx_i2c_acpi_ids[] = { {"BRCM9007", 0}, {"CAV9007", 0}, {} }; MODULE_DEVICE_TABLE(acpi, xlp9xx_i2c_acpi_ids); #endif static struct platform_driver xlp9xx_i2c_driver = { .probe = xlp9xx_i2c_probe, .remove = xlp9xx_i2c_remove, .driver = { .name = "xlp9xx-i2c", .of_match_table = xlp9xx_i2c_of_match, .acpi_match_table = ACPI_PTR(xlp9xx_i2c_acpi_ids), }, }; module_platform_driver(xlp9xx_i2c_driver); MODULE_AUTHOR("Subhendu Sekhar Behera <sbehera@broadcom.com>"); MODULE_DESCRIPTION("XLP9XX/5XX I2C Bus Controller Driver"); MODULE_LICENSE("GPL v2");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1