cregit-Linux how code gets into the kernel

Release 4.11 drivers/media/pci/solo6x10/solo6x10-i2c.c

/*
 * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
 *
 * Original author:
 * Ben Collins <bcollins@ubuntu.com>
 *
 * Additional work by:
 * John Brooks <john.brooks@bluecherry.net>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

/* XXX: The SOLO6x10 i2c does not have separate interrupts for each i2c
 * channel. The bus can only handle one i2c event at a time. The below handles
 * this all wrong. We should be using the status registers to see if the bus
 * is in use, and have a global lock to check the status register. Also,
 * the bulk of the work should be handled out-of-interrupt. The ugly loops
 * that occur during interrupt scare me. The ISR should merely signal
 * thread context, ACK the interrupt, and move on. -- BenC */

#include <linux/kernel.h>
#include <linux/sched/signal.h>

#include "solo6x10.h"


u8 solo_i2c_readbyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off) { struct i2c_msg msgs[2]; u8 data; msgs[0].flags = 0; msgs[0].addr = addr; msgs[0].len = 1; msgs[0].buf = &off; msgs[1].flags = I2C_M_RD; msgs[1].addr = addr; msgs[1].len = 1; msgs[1].buf = &data; i2c_transfer(&solo_dev->i2c_adap[id], msgs, 2); return data; }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Collins12099.17%150.00%
Krzysztof Hałasa10.83%150.00%
Total121100.00%2100.00%


void solo_i2c_writebyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off, u8 data) { struct i2c_msg msgs; u8 buf[2]; buf[0] = off; buf[1] = data; msgs.flags = 0; msgs.addr = addr; msgs.len = 2; msgs.buf = buf; i2c_transfer(&solo_dev->i2c_adap[id], &msgs, 1); }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Collins8598.84%150.00%
Krzysztof Hałasa11.16%150.00%
Total86100.00%2100.00%


static void solo_i2c_flush(struct solo_dev *solo_dev, int wr) { u32 ctrl; ctrl = SOLO_IIC_CH_SET(solo_dev->i2c_id); if (solo_dev->i2c_state == IIC_STATE_START) ctrl |= SOLO_IIC_START; if (wr) { ctrl |= SOLO_IIC_WRITE; } else { ctrl |= SOLO_IIC_READ; if (!(solo_dev->i2c_msg->flags & I2C_M_NO_RD_ACK)) ctrl |= SOLO_IIC_ACK_EN; } if (solo_dev->i2c_msg_ptr == solo_dev->i2c_msg->len) ctrl |= SOLO_IIC_STOP; solo_reg_write(solo_dev, SOLO_IIC_CTRL, ctrl); }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Collins9698.97%150.00%
Krzysztof Hałasa11.03%150.00%
Total97100.00%2100.00%


static void solo_i2c_start(struct solo_dev *solo_dev) { u32 addr = solo_dev->i2c_msg->addr << 1; if (solo_dev->i2c_msg->flags & I2C_M_RD) addr |= 1; solo_dev->i2c_state = IIC_STATE_START; solo_reg_write(solo_dev, SOLO_IIC_TXD, addr); solo_i2c_flush(solo_dev, 1); }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Collins5798.28%150.00%
Krzysztof Hałasa11.72%150.00%
Total58100.00%2100.00%


static void solo_i2c_stop(struct solo_dev *solo_dev) { solo_irq_off(solo_dev, SOLO_IRQ_IIC); solo_reg_write(solo_dev, SOLO_IIC_CTRL, 0); solo_dev->i2c_state = IIC_STATE_STOP; wake_up(&solo_dev->i2c_wait); }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Collins3995.12%150.00%
Krzysztof Hałasa24.88%150.00%
Total41100.00%2100.00%


static int solo_i2c_handle_read(struct solo_dev *solo_dev) { prepare_read: if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) { solo_i2c_flush(solo_dev, 0); return 0; } solo_dev->i2c_msg_ptr = 0; solo_dev->i2c_msg++; solo_dev->i2c_msg_num--; if (solo_dev->i2c_msg_num == 0) { solo_i2c_stop(solo_dev); return 0; } if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) { solo_i2c_start(solo_dev); } else { if (solo_dev->i2c_msg->flags & I2C_M_RD) goto prepare_read; else solo_i2c_stop(solo_dev); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Collins11599.14%150.00%
Krzysztof Hałasa10.86%150.00%
Total116100.00%2100.00%


static int solo_i2c_handle_write(struct solo_dev *solo_dev) { retry_write: if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) { solo_reg_write(solo_dev, SOLO_IIC_TXD, solo_dev->i2c_msg->buf[solo_dev->i2c_msg_ptr]); solo_dev->i2c_msg_ptr++; solo_i2c_flush(solo_dev, 1); return 0; } solo_dev->i2c_msg_ptr = 0; solo_dev->i2c_msg++; solo_dev->i2c_msg_num--; if (solo_dev->i2c_msg_num == 0) { solo_i2c_stop(solo_dev); return 0; } if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) { solo_i2c_start(solo_dev); } else { if (solo_dev->i2c_msg->flags & I2C_M_RD) solo_i2c_stop(solo_dev); else goto retry_write; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Collins13899.28%150.00%
Krzysztof Hałasa10.72%150.00%
Total139100.00%2100.00%


int solo_i2c_isr(struct solo_dev *solo_dev) { u32 status = solo_reg_read(solo_dev, SOLO_IIC_CTRL); int ret = -EINVAL; if (CHK_FLAGS(status, SOLO_IIC_STATE_TRNS | SOLO_IIC_STATE_SIG_ERR) || solo_dev->i2c_id < 0) { solo_i2c_stop(solo_dev); return -ENXIO; } switch (solo_dev->i2c_state) { case IIC_STATE_START: if (solo_dev->i2c_msg->flags & I2C_M_RD) { solo_dev->i2c_state = IIC_STATE_READ; ret = solo_i2c_handle_read(solo_dev); break; } solo_dev->i2c_state = IIC_STATE_WRITE; case IIC_STATE_WRITE: ret = solo_i2c_handle_write(solo_dev); break; case IIC_STATE_READ: solo_dev->i2c_msg->buf[solo_dev->i2c_msg_ptr] = solo_reg_read(solo_dev, SOLO_IIC_RXD); solo_dev->i2c_msg_ptr++; ret = solo_i2c_handle_read(solo_dev); break; default: solo_i2c_stop(solo_dev); } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Collins14596.03%125.00%
Hans Verkuil42.65%125.00%
Dan Carpenter10.66%125.00%
Krzysztof Hałasa10.66%125.00%
Total151100.00%4100.00%


static int solo_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) { struct solo_dev *solo_dev = adap->algo_data; unsigned long timeout; int ret; int i; DEFINE_WAIT(wait); for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { if (&solo_dev->i2c_adap[i] == adap) break; } if (i == SOLO_I2C_ADAPTERS) return num; /* XXX Right return value for failure? */ mutex_lock(&solo_dev->i2c_mutex); solo_dev->i2c_id = i; solo_dev->i2c_msg = msgs; solo_dev->i2c_msg_num = num; solo_dev->i2c_msg_ptr = 0; solo_reg_write(solo_dev, SOLO_IIC_CTRL, 0); solo_irq_on(solo_dev, SOLO_IRQ_IIC); solo_i2c_start(solo_dev); timeout = HZ / 2; for (;;) { prepare_to_wait(&solo_dev->i2c_wait, &wait, TASK_INTERRUPTIBLE); if (solo_dev->i2c_state == IIC_STATE_STOP) break; timeout = schedule_timeout(timeout); if (!timeout) break; if (signal_pending(current)) break; } finish_wait(&solo_dev->i2c_wait, &wait); ret = num - solo_dev->i2c_msg_num; solo_dev->i2c_state = IIC_STATE_IDLE; solo_dev->i2c_id = -1; mutex_unlock(&solo_dev->i2c_mutex); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Collins23098.71%250.00%
Krzysztof Hałasa20.86%125.00%
facugaich10.43%125.00%
Total233100.00%4100.00%


static u32 solo_i2c_functionality(struct i2c_adapter *adap) { return I2C_FUNC_I2C; }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Collins14100.00%1100.00%
Total14100.00%1100.00%

static const struct i2c_algorithm solo_i2c_algo = { .master_xfer = solo_i2c_master_xfer, .functionality = solo_i2c_functionality, };
int solo_i2c_init(struct solo_dev *solo_dev) { int i; int ret; solo_reg_write(solo_dev, SOLO_IIC_CFG, SOLO_IIC_PRESCALE(8) | SOLO_IIC_ENABLE); solo_dev->i2c_id = -1; solo_dev->i2c_state = IIC_STATE_IDLE; init_waitqueue_head(&solo_dev->i2c_wait); mutex_init(&solo_dev->i2c_mutex); for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { struct i2c_adapter *adap = &solo_dev->i2c_adap[i]; snprintf(adap->name, I2C_NAME_SIZE, "%s I2C %d", SOLO6X10_NAME, i); adap->algo = &solo_i2c_algo; adap->algo_data = solo_dev; adap->retries = 1; adap->dev.parent = &solo_dev->pdev->dev; ret = i2c_add_adapter(adap); if (ret) { adap->algo_data = NULL; break; } } if (ret) { for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { if (!solo_dev->i2c_adap[i].algo_data) break; i2c_del_adapter(&solo_dev->i2c_adap[i]); solo_dev->i2c_adap[i].algo_data = NULL; } return ret; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Collins21097.22%250.00%
facugaich41.85%125.00%
Krzysztof Hałasa20.93%125.00%
Total216100.00%4100.00%


void solo_i2c_exit(struct solo_dev *solo_dev) { int i; for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { if (!solo_dev->i2c_adap[i].algo_data) continue; i2c_del_adapter(&solo_dev->i2c_adap[i]); solo_dev->i2c_adap[i].algo_data = NULL; } }

Contributors

PersonTokensPropCommitsCommitProp
Benjamin Collins6298.41%150.00%
Krzysztof Hałasa11.59%150.00%
Total63100.00%2100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
Benjamin Collins133397.73%222.22%
Krzysztof Hałasa161.17%222.22%
Hans Verkuil60.44%222.22%
facugaich50.37%111.11%
Ingo Molnar30.22%111.11%
Dan Carpenter10.07%111.11%
Total1364100.00%9100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.