Re: [PATCH 1/2] i2c: Add Imagination Technologies I2C SCB driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Ezequiel,

> From: James Hogan <james.hogan@xxxxxxxxxx>
>
> Add support for the IMG I2C Serial Control Bus (SCB) found on the
> Pistachio and TZ1090 SoCs.
>
> Signed-off-by: James Hogan <james.hogan@xxxxxxxxxx>
> [Ezequiel: code cleaning and rebasing]
> Signed-off-by: Ezequiel Garcia <ezequiel.garcia@xxxxxxxxxx>

Looks mostly good.  A few comments below.

> diff --git a/drivers/i2c/busses/i2c-img-scb.c b/drivers/i2c/busses/i2c-img-scb.c
> new file mode 100644

> +#define INT_ENABLE_MASK_ATOMIC         (INT_TRANSACTION_DONE | \
> +                                        INT_SLAVE_EVENT      | \
> +                                        INT_ADDR_ACK_ERR     | \
> +                                        INT_WRITE_ACK_ERR    | \
> +                                        INT_ADDR_ACK_ERR)

INT_ADDR_ACK_ERR is repeated here.

> +/*
> + * Bits to return from interrupt handler functions for different modes.
> + * This delays completion until we've finished with the registers, so that the
> + * function waiting for completion can safely disable the clock to save power.
> + */
> +#define ISR_COMPLETE_M         0x80000000
> +#define ISR_FATAL_M            0x40000000
> +#define ISR_WAITSTOP           0x20000000
> +#define ISR_ATDATA_M           0x0ff00000
> +#define ISR_ATDATA_S           20
> +#define ISR_ATCMD_M            0x000f0000
> +#define ISR_ATCMD_S            16
> +#define ISR_STATUS_M           0x0000ffff      /* contains +ve errno */
> +#define ISR_COMPLETE(ERR)      (ISR_COMPLETE_M | (ISR_STATUS_M & (ERR)))
> +#define ISR_FATAL(ERR)         (ISR_COMPLETE(ERR) | ISR_FATAL_M)

Macro parameters are generally lower-case.

> +#define ISR_ATOMIC(CMD, DATA)  ((ISR_ATCMD_M & ((CMD) << ISR_ATCMD_S)) \
> +                               | (ISR_ATDATA_M & ((DATA) << ISR_ATDATA_S)))

What's the point of encoding the atomic command and data here?  I
don't see them being extracted from the return value anywhere.

> +struct img_i2c {
> +       struct i2c_adapter adap;
> +
> +       void __iomem *base;
> +
> +       /*
> +        * The clock is used to get the input frequency, and to disable it
> +        * after every set of transactions to save some power.
> +        */
> +       struct clk *clk;
> +       unsigned int bitrate;
> +       unsigned int busdelay;
> +       bool need_wr_rd_fence;
> +
> +       /* state */
> +       struct completion msg_complete;
> +       spinlock_t lock;        /* lock before doing anything with the state */
> +       struct i2c_msg msg;
> +
> +       /* After the last transaction, wait for a stop bit */
> +       bool last_msg;
> +       int msg_status;
> +
> +       enum img_i2c_mode mode;
> +       u32 int_enable;         /* depends on mode */
> +       u32 line_status;        /* line status over command */
> +
> +       /*
> +        * To avoid slave event interrupts in automatic mode, use a timer to
> +        * poll the abort condition if we don't get an interrupt for too long.
> +        */

Why would polling be better than taking the interrupt?  Are an
excessive number of interrupts generated during normal operation?

> +       struct timer_list check_timer;
> +       bool t_halt;
> +
> +       /* atomic mode state */
> +       bool at_t_done;
> +       bool at_slave_event;
> +       int at_cur_cmd;
> +       u8 at_cur_data;
> +
> +       /* Sequence: either reset or stop. See img_i2c_sequence. */
> +       u8 *seq;
> +
> +       /* raw mode */
> +       unsigned int raw_timeout;
> +};

> +static void img_i2c_writel(void __iomem *base, u32 offset, u32 value)
> +{
> +       writel(value, base + offset);
> +}
> +
> +static u32 img_i2c_readl(void __iomem *base, u32 offset)
> +{
> +       return readl(base + offset);
> +}

These don't really add anything if they require the base address.  It
would be more useful if they took a struct img_i2c and did the
dereference.

> +/*
> + * Timer function to check if something has gone wrong in automatic mode (so we
> + * don't have to handle so many interrupts just to catch an exception).
> + */
> +static void img_i2c_check_timer(unsigned long arg)

When are slave event interrupts generated during normal operation?
It's not clear from the TRM I have.

> +/* Force a bus reset sequence and wait for it to complete */
> +static void i2c_img_reset_bus(struct img_i2c *i2c)

Perhaps call this img_i2c_reset_bus() to match the convention the rest
of the file is using?

> +static int img_i2c_init(struct img_i2c *i2c)
> +{
> +       int opt_inc, data, prescale, inc, filt, clk_period, int_bitrate;
> +       int clk_khz, bitrate_khz, tckh, tckl, tsdh, i;

Most of these should be unsigned, I think.

> +       struct img_i2c_timings timing;
> +       u32 rev;
> +
> +       clk_prepare_enable(i2c->clk);
> +
> +       rev = img_i2c_readl(i2c->base, SCB_CORE_REV_REG);
> +       if ((rev & 0x00ffffff) < 0x00020200) {
> +               dev_info(i2c->adap.dev.parent,
> +                        "Unknown hardware revision (%d.%d.%d.%d)\n",
> +                        (rev >> 24) & 0xff, (rev >> 16) & 0xff,
> +                        (rev >> 8) & 0xff, rev & 0xff);
> +               clk_disable_unprepare(i2c->clk);
> +               return -EINVAL;
> +       }
> +
> +       if (rev == REL_SOC_IP_SCB_2_2_1)
> +               i2c->need_wr_rd_fence = true;
> +
> +       bitrate_khz = i2c->bitrate / 1000;
> +       clk_khz = clk_get_rate(i2c->clk) / 1000;
> +
> +       /* Determine what mode we're in from the bitrate */
> +       timing = timings[0];
> +       for (i = 0; i < ARRAY_SIZE(timings); i++) {
> +               if (i2c->bitrate <= timings[i].max_bitrate) {
> +                       timing = timings[i];
> +                       break;
> +               }
> +       }
> +
> +       /*
> +        * Worst incs are 1 (innacurate) and 16*256 (irregular).
> +        * So a sensible inc is the logarithmic mean: 64 (2^6), which is
> +        * in the middle of the valid range (0-127).
> +        */
> +       opt_inc = 64;
> +
> +       /* Find the prescale that would give us that inc (approx delay = 0) */
> +       prescale = opt_inc * clk_khz / (256 * 16 * bitrate_khz);
> +       if (prescale > 8)
> +               prescale = 8;
> +       else if (prescale < 1)
> +               prescale = 1;
> +       clk_khz /= prescale;
> +
> +       /* Setup the clock increment value */
> +       inc = ((256 * 16 * bitrate_khz) /
> +              (clk_khz - (16 * bitrate_khz * (clk_khz / 1000) *
> +                          i2c->busdelay) / 10000));
> +       /* Setup the filter clock value */
> +       if (clk_khz < 20000) {
> +               /* Filter disable */
> +               filt = 0x8000;
> +       } else if (clk_khz < 40000) {
> +               /* Filter bypass */
> +               filt = 0x4000;
> +       } else {
> +               /* Calculate filter clock */
> +               filt = ((640000) / ((clk_khz / 1000) *
> +                                   (250 - i2c->busdelay)));
> +               if ((640000) % ((clk_khz / 1000) *
> +                               (250 - i2c->busdelay))) {
> +                       /* Scale up */
> +                       inc++;
> +               }
> +               if (filt > 0x7f)
> +                       filt = 0x7f;
> +       }
> +       data = ((filt & 0xffff) << 16) | ((inc & 0x7f) << 8) | (prescale - 1);
> +       img_i2c_writel(i2c->base, SCB_CLK_SET_REG, data);

These masks and shifts should probably be #defines, as well as the
FILT_DISABLE and FILT_BYPASS bits.

> +static int i2c_img_probe(struct platform_device *pdev)
> +{
> +       struct device_node *node = pdev->dev.of_node;
> +       struct img_i2c *i2c;
> +       struct resource *res;
> +       int irq, ret;
> +       u32 val;
> +
> +       i2c = devm_kzalloc(&pdev->dev, sizeof(struct img_i2c), GFP_KERNEL);
> +       if (!i2c)
> +               return -ENOMEM;
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       i2c->base = devm_ioremap_resource(&pdev->dev, res);
> +       if (IS_ERR(i2c->base))
> +               return PTR_ERR(i2c->base);
> +
> +       irq = platform_get_irq(pdev, 0);
> +       if (irq < 0) {
> +               dev_err(&pdev->dev, "can't get irq number\n");
> +               return irq;
> +       }
> +
> +       /*
> +        * Get the bus number from the devicetree. Other I2C adapters may be
> +        * reserved for non-Linux core. Instead of letting Linux pick any
> +        * number, it's useful to fix the bus number so users get a consistent
> +        * view.
> +        */
> +       ret = of_property_read_u32(node, "linux,i2c-index", &val);
> +       if (!ret)
> +               i2c->adap.nr = val;
> +       else
> +               i2c->adap.nr = pdev->id;
> +
> +       snprintf(i2c->adap.name, sizeof(i2c->adap.name),
> +                "IMG i2c%d", i2c->adap.nr);
> +
> +       i2c->clk = devm_clk_get(&pdev->dev, NULL);
> +       if (IS_ERR(i2c->clk)) {
> +               dev_err(&pdev->dev, "can't get clock\n");
> +               return PTR_ERR(i2c->clk);
> +       }
> +
> +       ret = devm_request_irq(&pdev->dev, irq, img_i2c_isr, 0,
> +                              pdev->name, i2c);
> +       if (ret) {
> +               dev_err(&pdev->dev, "can't request irq %d\n", irq);
> +               return ret;
> +       }
> +
> +       /* Set up the exception check timer */
> +       init_timer(&i2c->check_timer);
> +       i2c->check_timer.function = img_i2c_check_timer;
> +       i2c->check_timer.data = (unsigned long)i2c;
> +
> +       i2c->bitrate = timings[0].max_bitrate;
> +       if (!of_property_read_u32(node, "clock-frequency", &val))
> +               i2c->bitrate = val;
> +
> +       i2c->busdelay = 0;
> +       if (!of_property_read_u32(node, "bus-delay", &val))
> +               i2c->busdelay = val;

"bus-delay" does not appear to be a generic property.  It should
probably have an "img" prefix.

> +static struct platform_driver i2c_img_driver = {
> +       .driver = {
> +               .name           = "img-i2c-scb",
> +               .of_match_table = i2c_img_match,
> +               .pm             = &img_i2c_pm,
> +       },
> +       .probe = i2c_img_probe,
> +       .remove = i2c_img_remove,
> +};
> +
> +module_platform_driver(i2c_img_driver);

No newline before module_platform_driver().
--
To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux GPIO]     [Linux SPI]     [Linux Hardward Monitoring]     [LM Sensors]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux