1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Wondermedia I2C Master Mode Driver 4 * 5 * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz> 6 * 7 * Derived from GPLv2+ licensed source: 8 * - Copyright (C) 2008 WonderMedia Technologies, Inc. 9 */ 10 11 #include <linux/clk.h> 12 #include <linux/of.h> 13 #include <linux/of_address.h> 14 #include "i2c-viai2c-common.h" 15 16 #define REG_SLAVE_CR 0x10 17 #define REG_SLAVE_SR 0x12 18 #define REG_SLAVE_ISR 0x14 19 #define REG_SLAVE_IMR 0x16 20 #define REG_SLAVE_DR 0x18 21 #define REG_SLAVE_TR 0x1A 22 23 /* REG_TR */ 24 #define SCL_TIMEOUT(x) (((x) & 0xFF) << 8) 25 #define TR_STD 0x0064 26 #define TR_HS 0x0019 27 28 /* REG_MCR */ 29 #define MCR_APB_96M 7 30 #define MCR_APB_166M 12 31 32 static u32 wmt_i2c_func(struct i2c_adapter *adap) 33 { 34 return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_NOSTART; 35 } 36 37 static const struct i2c_algorithm wmt_i2c_algo = { 38 .master_xfer = viai2c_xfer, 39 .functionality = wmt_i2c_func, 40 }; 41 42 static int wmt_i2c_reset_hardware(struct viai2c *i2c) 43 { 44 int err; 45 46 err = clk_prepare_enable(i2c->clk); 47 if (err) { 48 dev_err(i2c->dev, "failed to enable clock\n"); 49 return err; 50 } 51 52 err = clk_set_rate(i2c->clk, 20000000); 53 if (err) { 54 dev_err(i2c->dev, "failed to set clock = 20Mhz\n"); 55 clk_disable_unprepare(i2c->clk); 56 return err; 57 } 58 59 writew(0, i2c->base + VIAI2C_REG_CR); 60 writew(MCR_APB_166M, i2c->base + VIAI2C_REG_MCR); 61 writew(VIAI2C_ISR_MASK_ALL, i2c->base + VIAI2C_REG_ISR); 62 writew(VIAI2C_IMR_ENABLE_ALL, i2c->base + VIAI2C_REG_IMR); 63 writew(VIAI2C_CR_ENABLE, i2c->base + VIAI2C_REG_CR); 64 readw(i2c->base + VIAI2C_REG_CSR); /* read clear */ 65 writew(VIAI2C_ISR_MASK_ALL, i2c->base + VIAI2C_REG_ISR); 66 67 if (i2c->tcr == VIAI2C_TCR_FAST) 68 writew(SCL_TIMEOUT(128) | TR_HS, i2c->base + VIAI2C_REG_TR); 69 else 70 writew(SCL_TIMEOUT(128) | TR_STD, i2c->base + VIAI2C_REG_TR); 71 72 return 0; 73 } 74 75 static int wmt_i2c_probe(struct platform_device *pdev) 76 { 77 struct device_node *np = pdev->dev.of_node; 78 struct viai2c *i2c; 79 struct i2c_adapter *adap; 80 int err; 81 u32 clk_rate; 82 83 err = viai2c_init(pdev, &i2c, VIAI2C_PLAT_WMT); 84 if (err) 85 return err; 86 87 i2c->clk = of_clk_get(np, 0); 88 if (IS_ERR(i2c->clk)) { 89 dev_err(&pdev->dev, "unable to request clock\n"); 90 return PTR_ERR(i2c->clk); 91 } 92 93 err = of_property_read_u32(np, "clock-frequency", &clk_rate); 94 if (!err && clk_rate == I2C_MAX_FAST_MODE_FREQ) 95 i2c->tcr = VIAI2C_TCR_FAST; 96 97 adap = &i2c->adapter; 98 i2c_set_adapdata(adap, i2c); 99 strscpy(adap->name, "WMT I2C adapter", sizeof(adap->name)); 100 adap->owner = THIS_MODULE; 101 adap->algo = &wmt_i2c_algo; 102 adap->dev.parent = &pdev->dev; 103 adap->dev.of_node = pdev->dev.of_node; 104 105 err = wmt_i2c_reset_hardware(i2c); 106 if (err) { 107 dev_err(&pdev->dev, "error initializing hardware\n"); 108 return err; 109 } 110 111 err = i2c_add_adapter(adap); 112 if (err) 113 /* wmt_i2c_reset_hardware() enables i2c_dev->clk */ 114 clk_disable_unprepare(i2c->clk); 115 116 return err; 117 } 118 119 static void wmt_i2c_remove(struct platform_device *pdev) 120 { 121 struct viai2c *i2c = platform_get_drvdata(pdev); 122 123 /* Disable interrupts, clock and delete adapter */ 124 writew(0, i2c->base + VIAI2C_REG_IMR); 125 clk_disable_unprepare(i2c->clk); 126 i2c_del_adapter(&i2c->adapter); 127 } 128 129 static const struct of_device_id wmt_i2c_dt_ids[] = { 130 { .compatible = "wm,wm8505-i2c" }, 131 { /* Sentinel */ }, 132 }; 133 134 static struct platform_driver wmt_i2c_driver = { 135 .probe = wmt_i2c_probe, 136 .remove_new = wmt_i2c_remove, 137 .driver = { 138 .name = "wmt-i2c", 139 .of_match_table = wmt_i2c_dt_ids, 140 }, 141 }; 142 143 module_platform_driver(wmt_i2c_driver); 144 145 MODULE_DESCRIPTION("Wondermedia I2C master-mode bus adapter"); 146 MODULE_AUTHOR("Tony Prisk <linux@prisktech.co.nz>"); 147 MODULE_LICENSE("GPL"); 148 MODULE_DEVICE_TABLE(of, wmt_i2c_dt_ids); 149